ugly-app 0.1.417 → 0.1.419

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.
@@ -1 +1 @@
1
- {"version":3,"file":"authCommands.d.ts","sourceRoot":"","sources":["../../src/cli/authCommands.ts"],"names":[],"mappings":"AAIA,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAqC5C;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CA6C5C"}
1
+ {"version":3,"file":"authCommands.d.ts","sourceRoot":"","sources":["../../src/cli/authCommands.ts"],"names":[],"mappings":"AAKA,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAqC5C;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAwD5C"}
@@ -1,6 +1,7 @@
1
1
  import { nanoid } from 'nanoid';
2
2
  import { getUglyBotUrl } from '../shared/uglyBotUrl.js';
3
3
  import { initDataProxy, pgQuery, stopDataProxy, ensureTable } from '../server/DataProxyClient.js';
4
+ import { migrateTokensFromUglyApp, readAuthToken } from './authStore.js';
4
5
  export async function createAccount(email, name) {
5
6
  const proxyUrl = process.env['DATA_PROXY_URL'] ?? 'ws://localhost:4200';
6
7
  const proxyToken = process.env['DATA_PROXY_TOKEN'] ?? '';
@@ -33,13 +34,24 @@ export async function createAccount(email, name) {
33
34
  export async function createBot(slug, name) {
34
35
  const fs = await import('fs');
35
36
  let projectToken = '';
36
- // Read from .uglyapp config
37
+ let projectId = '';
38
+ // Migrate any legacy .uglyapp tokens into ~/.ugly-bot/ first.
39
+ migrateTokensFromUglyApp(process.cwd());
40
+ // Read projectId (and any leftover inline token) from .uglyapp.
37
41
  try {
38
42
  const config = JSON.parse(fs.readFileSync('.uglyapp', 'utf-8'));
43
+ if (typeof config.projectId === 'string')
44
+ projectId = config.projectId;
39
45
  if (typeof config.uglyBotToken === 'string')
40
46
  projectToken = config.uglyBotToken;
41
47
  }
42
48
  catch { /* no .uglyapp */ }
49
+ // Primary source: ~/.ugly-bot/ auth store (where `npx ugly-app login` writes tokens).
50
+ if (!projectToken && projectId) {
51
+ const stored = readAuthToken(projectId);
52
+ if (stored)
53
+ projectToken = stored;
54
+ }
43
55
  // Fall back to .env for backwards compatibility
44
56
  if (!projectToken) {
45
57
  try {
@@ -51,7 +63,7 @@ export async function createBot(slug, name) {
51
63
  catch { /* no .env either */ }
52
64
  }
53
65
  if (!projectToken) {
54
- throw new Error('uglyBotToken not found in .uglyapp. Run "npx ugly-app login" first.');
66
+ throw new Error('No project auth token found. Run "npx ugly-app login" in this project to authenticate.');
55
67
  }
56
68
  const serverUrl = getUglyBotUrl();
57
69
  const res = await fetch(`${serverUrl}/request`, {
@@ -1 +1 @@
1
- {"version":3,"file":"authCommands.js","sourceRoot":"","sources":["../../src/cli/authCommands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"authCommands.js","sourceRoot":"","sources":["../../src/cli/authCommands.ts"],"names":[],"mappings":"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"}
@@ -8,5 +8,15 @@
8
8
  * lines and a trailing "next steps" section so an agent can parse it
9
9
  * by grep if it needs to.
10
10
  */
11
- export declare function runDoctor(): Promise<void>;
11
+ export interface RunDoctorOptions {
12
+ /** Override for tests. Defaults to process.cwd(). */
13
+ cwd?: string;
14
+ /** Override for tests. Defaults to os.homedir(). */
15
+ homeDir?: string;
16
+ /** Skip the live DataProxy / migration probes. Defaults to false. */
17
+ skipLiveChecks?: boolean;
18
+ /** Sink for output lines. Defaults to console.log. */
19
+ print?: (line: string) => void;
20
+ }
21
+ export declare function runDoctor(options?: RunDoctorOptions): Promise<void>;
12
22
  //# sourceMappingURL=doctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAaH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAmJ/C"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sDAAsD;IACtD,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,wBAAsB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0I7E"}
@@ -9,11 +9,15 @@
9
9
  * by grep if it needs to.
10
10
  */
11
11
  import fs from 'fs';
12
+ import os from 'os';
12
13
  import path from 'path';
13
14
  import { readUglyAppConfig } from './uglyappConfig.js';
14
- import { readAuthToken, readLocalToken } from './authStore.js';
15
- export async function runDoctor() {
16
- const cwd = process.cwd();
15
+ import { probeAuth } from './probeAuth.js';
16
+ export async function runDoctor(options = {}) {
17
+ const cwd = options.cwd ?? process.cwd();
18
+ const homeDir = options.homeDir ?? os.homedir();
19
+ const print = options.print ?? ((s) => console.log(s));
20
+ const skipLive = options.skipLiveChecks ?? false;
17
21
  const lines = [];
18
22
  const nextSteps = [];
19
23
  // 1. .uglyapp
@@ -33,44 +37,19 @@ export async function runDoctor() {
33
37
  detail: `projectId=${config.projectId} localPort=${config.localPort}${config.domain ? ` domain=${config.domain}` : ''}`,
34
38
  });
35
39
  }
36
- // 2. Auth tokens
40
+ // 2. Auth tokens — global user identity + per-project cached tokens.
41
+ // probeAuth() is the single source of truth so test code can exercise
42
+ // the same logic without spinning up a DataProxy.
37
43
  const isLocal = process.env['UGLY_BOT_LOCAL'] === '1';
38
- if (config) {
39
- const cloudToken = readAuthToken(config.projectId) ?? config.uglyBotToken;
40
- const localToken = readLocalToken(config.projectId) ?? config.localToken;
41
- if (cloudToken) {
42
- lines.push({
43
- status: 'ok',
44
- label: 'auth (cloud)',
45
- detail: `project token present (${cloudToken.slice(0, 8)}…)`,
46
- });
47
- }
48
- else {
49
- lines.push({
50
- status: isLocal ? 'info' : 'warn',
51
- label: 'auth (cloud)',
52
- detail: 'no project token for ugly.bot cloud',
53
- });
54
- if (!isLocal)
55
- nextSteps.push('Run `npx ugly-app login` to mint a project token');
56
- }
57
- if (localToken) {
58
- lines.push({
59
- status: 'ok',
60
- label: 'auth (local)',
61
- detail: `local project token present (${localToken.slice(0, 8)}…)`,
62
- });
63
- }
64
- else {
65
- lines.push({
66
- status: isLocal ? 'warn' : 'info',
67
- label: 'auth (local)',
68
- detail: 'no local ugly.bot token',
69
- });
70
- if (isLocal)
71
- nextSteps.push('Run `UGLY_BOT_LOCAL=1 npx ugly-app login` to mint a local token');
72
- }
73
- }
44
+ const authProbe = probeAuth({
45
+ homeDir,
46
+ projectId: config?.projectId ?? null,
47
+ isLocal,
48
+ });
49
+ for (const l of authProbe.lines)
50
+ lines.push(l);
51
+ for (const s of authProbe.nextSteps)
52
+ nextSteps.push(s);
74
53
  // 3. DataProxy / env inheritance (pre-ensureInfra snapshot)
75
54
  const envUrl = process.env['DATA_PROXY_URL'];
76
55
  const envToken = process.env['DATA_PROXY_TOKEN'];
@@ -117,7 +96,7 @@ export async function runDoctor() {
117
96
  // 5. Live DataProxy connection check (only when .uglyapp + some token).
118
97
  // Gated on config presence so doctor is fast in a half-configured app
119
98
  // instead of blocking on a tunnel handshake that can't succeed.
120
- if (config) {
99
+ if (config && !skipLive) {
121
100
  const connectCheck = await safeConnectCheck(cwd);
122
101
  lines.push({
123
102
  status: connectCheck.ok ? 'ok' : 'error',
@@ -140,8 +119,8 @@ export async function runDoctor() {
140
119
  }
141
120
  }
142
121
  // Print the report
143
- console.log('ugly-app doctor — child app diagnostics');
144
- console.log('═'.repeat(50));
122
+ print('ugly-app doctor — child app diagnostics');
123
+ print('═'.repeat(50));
145
124
  for (const l of lines) {
146
125
  const icon = l.status === 'ok'
147
126
  ? '✓'
@@ -150,17 +129,27 @@ export async function runDoctor() {
150
129
  : l.status === 'error'
151
130
  ? '✗'
152
131
  : '·';
153
- console.log(`${icon} ${l.label.padEnd(18)} ${l.detail}`);
132
+ print(`${icon} ${l.label.padEnd(18)} ${l.detail}`);
133
+ }
134
+ // Agent-ready summary — a single grep-friendly line so headless
135
+ // tooling (the bot-swarm skill, evals, CI) can decide whether to
136
+ // proceed without parsing the human-formatted lines above.
137
+ print('');
138
+ if (authProbe.agentReady) {
139
+ print('agent-ready: yes');
140
+ }
141
+ else {
142
+ print(`agent-ready: no — ${authProbe.agentBlockedReason ?? 'auth incomplete'}`);
154
143
  }
155
144
  if (nextSteps.length > 0) {
156
- console.log('');
157
- console.log('Next steps:');
145
+ print('');
146
+ print('Next steps:');
158
147
  for (const s of nextSteps)
159
- console.log(` • ${s}`);
148
+ print(` • ${s}`);
160
149
  }
161
150
  else {
162
- console.log('');
163
- console.log('Next steps: nothing obvious — env looks healthy.');
151
+ print('');
152
+ print('Next steps: nothing obvious — env looks healthy.');
164
153
  }
165
154
  }
166
155
  function safeReadConfig(cwd) {
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ export type AuthStatus = 'ok' | 'warn' | 'error' | 'info';
2
+ export interface AuthLine {
3
+ status: AuthStatus;
4
+ label: string;
5
+ detail: string;
6
+ }
7
+ export interface ProbeAuthInput {
8
+ homeDir: string;
9
+ projectId: string | null;
10
+ /** True when running against localhost ugly.bot (UGLY_BOT_LOCAL=1). */
11
+ isLocal: boolean;
12
+ }
13
+ export interface ProbeAuthResult {
14
+ /** Human-readable lines, in the order they should be printed. */
15
+ lines: AuthLine[];
16
+ /** Hints to show under "Next steps:". */
17
+ nextSteps: string[];
18
+ /**
19
+ * True when the on-disk state is sufficient for a headless agent to
20
+ * proceed: at minimum a non-expired global user token exists, OR a
21
+ * non-expired project token of the right kind (cloud/local) exists.
22
+ * Project tokens are auto-minted from the global user token on first
23
+ * run, so the global user token is the real prerequisite.
24
+ */
25
+ agentReady: boolean;
26
+ /** When agentReady is false, a one-line reason. */
27
+ agentBlockedReason?: string;
28
+ /**
29
+ * The token an agent should put in `UGLY_BOT_TOKEN`, if one is
30
+ * available. Prefers the project token (matches what the framework
31
+ * uses at runtime); falls back to the global user token.
32
+ */
33
+ agentToken?: string;
34
+ }
35
+ /**
36
+ * Inspects the JWT exp claim without verifying the signature. Returns:
37
+ * - 'valid' : exp present and at least 60s in the future
38
+ * - 'expired' : exp present and in the past (or within 60s)
39
+ * - 'unknown' : token doesn't have a parseable exp — treat as valid
40
+ * since some service tokens are intentionally non-expiring
41
+ */
42
+ export declare function inspectJwt(token: string): 'valid' | 'expired' | 'unknown';
43
+ export declare function probeAuth(input: ProbeAuthInput): ProbeAuthResult;
44
+ //# sourceMappingURL=probeAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probeAuth.d.ts","sourceRoot":"","sources":["../../src/cli/probeAuth.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,yCAAyC;IACzC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;;;OAMG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB,mDAAmD;IACnD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAyBD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAYzE;AAyCD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CA0KhE"}
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Pure auth-state probe used by `ugly-app doctor` (and by tests).
3
+ *
4
+ * Reads the on-disk auth files under a given home directory and returns
5
+ * a structured report. No side effects, no DataProxy, no network.
6
+ *
7
+ * ~/.ugly-bot/auth.json — global user identity ({ token, userId, serverUrl })
8
+ * ~/.ugly-bot/<projectId>.json — cloud project token ({ token })
9
+ * ~/.ugly-bot/<projectId>.local.json — local project token ({ token })
10
+ *
11
+ * The doctor previously only checked the project-level files and
12
+ * reported "no project token for cloud" as a warning even when a valid
13
+ * global user identity existed (in which case `dev` / `auth:create-bot`
14
+ * will mint a project token automatically on first run). Headless
15
+ * agents reading the report concluded auth was broken and got stuck on
16
+ * `npx ugly-app login`, which needs a browser.
17
+ */
18
+ import fs from 'node:fs';
19
+ import path from 'node:path';
20
+ function readJson(filePath) {
21
+ try {
22
+ const raw = fs.readFileSync(filePath, 'utf-8');
23
+ try {
24
+ const parsed = JSON.parse(raw);
25
+ if (parsed && typeof parsed === 'object') {
26
+ return { data: parsed, malformed: false };
27
+ }
28
+ return { data: null, malformed: true };
29
+ }
30
+ catch {
31
+ return { data: null, malformed: true };
32
+ }
33
+ }
34
+ catch {
35
+ return { data: null, malformed: false };
36
+ }
37
+ }
38
+ /**
39
+ * Inspects the JWT exp claim without verifying the signature. Returns:
40
+ * - 'valid' : exp present and at least 60s in the future
41
+ * - 'expired' : exp present and in the past (or within 60s)
42
+ * - 'unknown' : token doesn't have a parseable exp — treat as valid
43
+ * since some service tokens are intentionally non-expiring
44
+ */
45
+ export function inspectJwt(token) {
46
+ const parts = token.split('.');
47
+ if (parts.length !== 3)
48
+ return 'unknown';
49
+ try {
50
+ const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
51
+ if (typeof payload.exp !== 'number')
52
+ return 'unknown';
53
+ return Date.now() / 1000 < payload.exp - 60 ? 'valid' : 'expired';
54
+ }
55
+ catch {
56
+ return 'unknown';
57
+ }
58
+ }
59
+ function readGlobalAuth(homeDir) {
60
+ const file = path.join(homeDir, '.ugly-bot', 'auth.json');
61
+ const { data, malformed } = readJson(file);
62
+ if (!data)
63
+ return { auth: null, malformed };
64
+ if (typeof data.token !== 'string' ||
65
+ typeof data.userId !== 'string' ||
66
+ typeof data.serverUrl !== 'string') {
67
+ return { auth: null, malformed: true };
68
+ }
69
+ return {
70
+ auth: { token: data.token, userId: data.userId, serverUrl: data.serverUrl },
71
+ malformed: false,
72
+ };
73
+ }
74
+ function readProjectToken(homeDir, projectId, mode) {
75
+ const suffix = mode === 'local' ? '.local.json' : '.json';
76
+ const file = path.join(homeDir, '.ugly-bot', `${projectId}${suffix}`);
77
+ const { data, malformed } = readJson(file);
78
+ if (!data)
79
+ return { token: null, malformed };
80
+ if (typeof data.token !== 'string')
81
+ return { token: null, malformed: true };
82
+ return { token: data.token, malformed: false };
83
+ }
84
+ export function probeAuth(input) {
85
+ const { homeDir, projectId, isLocal } = input;
86
+ const lines = [];
87
+ const nextSteps = [];
88
+ // 1. Global user identity (~/.ugly-bot/auth.json).
89
+ const { auth: globalAuth, malformed: globalMalformed } = readGlobalAuth(homeDir);
90
+ let globalValid = false;
91
+ if (globalAuth) {
92
+ const exp = inspectJwt(globalAuth.token);
93
+ if (exp === 'expired') {
94
+ lines.push({
95
+ status: 'warn',
96
+ label: 'auth (user)',
97
+ detail: `token expired (userId=${globalAuth.userId})`,
98
+ });
99
+ nextSteps.push('Run `npx ugly-app login` to refresh your global user token');
100
+ }
101
+ else {
102
+ globalValid = true;
103
+ lines.push({
104
+ status: 'ok',
105
+ label: 'auth (user)',
106
+ detail: `signed in as ${globalAuth.userId} (${globalAuth.serverUrl})`,
107
+ });
108
+ }
109
+ }
110
+ else if (globalMalformed) {
111
+ lines.push({
112
+ status: 'error',
113
+ label: 'auth (user)',
114
+ detail: '~/.ugly-bot/auth.json is malformed or missing required fields',
115
+ });
116
+ nextSteps.push('Delete ~/.ugly-bot/auth.json and re-run `npx ugly-app login`');
117
+ }
118
+ else {
119
+ lines.push({
120
+ status: 'error',
121
+ label: 'auth (user)',
122
+ detail: 'not signed in (no ~/.ugly-bot/auth.json)',
123
+ });
124
+ nextSteps.push('Run `npx ugly-app login` once on the host (interactive, opens a browser) — required even for headless agents');
125
+ }
126
+ // 2. Project tokens. Without a .uglyapp we have no projectId to look up.
127
+ let cloudToken = null;
128
+ let cloudExpired = false;
129
+ let localToken = null;
130
+ let localExpired = false;
131
+ if (projectId) {
132
+ const cloud = readProjectToken(homeDir, projectId, 'cloud');
133
+ const local = readProjectToken(homeDir, projectId, 'local');
134
+ if (cloud.token) {
135
+ const exp = inspectJwt(cloud.token);
136
+ if (exp === 'expired') {
137
+ cloudExpired = true;
138
+ lines.push({
139
+ status: 'warn',
140
+ label: 'auth (cloud)',
141
+ detail: `cached project token expired (${cloud.token.slice(0, 8)}…)`,
142
+ });
143
+ if (!isLocal && globalValid) {
144
+ nextSteps.push('Stale cloud project token — `ugly-app dev` will re-mint it from your user token');
145
+ }
146
+ }
147
+ else {
148
+ cloudToken = cloud.token;
149
+ lines.push({
150
+ status: 'ok',
151
+ label: 'auth (cloud)',
152
+ detail: `project token present (${cloud.token.slice(0, 8)}…)`,
153
+ });
154
+ }
155
+ }
156
+ else if (cloud.malformed) {
157
+ lines.push({
158
+ status: 'warn',
159
+ label: 'auth (cloud)',
160
+ detail: `~/.ugly-bot/${projectId}.json is malformed`,
161
+ });
162
+ nextSteps.push(`Delete ~/.ugly-bot/${projectId}.json and re-run \`npx ugly-app dev\` to re-mint`);
163
+ }
164
+ else if (globalValid && !isLocal) {
165
+ // Common case: never run before. Don't scare the agent — it's auto-minted.
166
+ lines.push({
167
+ status: 'info',
168
+ label: 'auth (cloud)',
169
+ detail: 'no cached project token — will be minted from user token on first run',
170
+ });
171
+ }
172
+ else {
173
+ lines.push({
174
+ status: isLocal ? 'info' : 'warn',
175
+ label: 'auth (cloud)',
176
+ detail: 'no project token for ugly.bot cloud',
177
+ });
178
+ if (!isLocal && !globalValid) {
179
+ // The "log in first" hint already came from the user line; don't duplicate.
180
+ }
181
+ }
182
+ if (local.token) {
183
+ const exp = inspectJwt(local.token);
184
+ if (exp === 'expired') {
185
+ localExpired = true;
186
+ lines.push({
187
+ status: isLocal ? 'warn' : 'info',
188
+ label: 'auth (local)',
189
+ detail: `cached local token expired (${local.token.slice(0, 8)}…)`,
190
+ });
191
+ if (isLocal) {
192
+ nextSteps.push('Stale local project token — `UGLY_BOT_LOCAL=1 npx ugly-app dev` will re-mint it');
193
+ }
194
+ }
195
+ else {
196
+ localToken = local.token;
197
+ lines.push({
198
+ status: 'ok',
199
+ label: 'auth (local)',
200
+ detail: `local project token present (${local.token.slice(0, 8)}…)`,
201
+ });
202
+ }
203
+ }
204
+ else if (local.malformed) {
205
+ lines.push({
206
+ status: 'warn',
207
+ label: 'auth (local)',
208
+ detail: `~/.ugly-bot/${projectId}.local.json is malformed`,
209
+ });
210
+ }
211
+ else {
212
+ lines.push({
213
+ status: isLocal ? (globalValid ? 'info' : 'warn') : 'info',
214
+ label: 'auth (local)',
215
+ detail: isLocal && globalValid
216
+ ? 'no cached local project token — will be minted on first local run'
217
+ : 'no local ugly.bot token',
218
+ });
219
+ }
220
+ }
221
+ // 3. Agent-ready summary.
222
+ // For headless tooling, the global user token is what unblocks
223
+ // everything: project tokens are derived. If we're in local mode we
224
+ // still want the global token because the local project token is
225
+ // minted from it.
226
+ let agentReady = false;
227
+ let agentBlockedReason;
228
+ let agentToken;
229
+ const liveCloud = cloudToken && !cloudExpired ? cloudToken : null;
230
+ const liveLocal = localToken && !localExpired ? localToken : null;
231
+ if (globalValid) {
232
+ agentReady = true;
233
+ agentToken = isLocal ? (liveLocal ?? globalAuth?.token) : (liveCloud ?? globalAuth?.token);
234
+ }
235
+ else if (liveCloud || liveLocal) {
236
+ // Edge case: project tokens cached but global identity gone. The
237
+ // project tokens still work for `auth:create-bot` etc., so don't
238
+ // block, but flag it.
239
+ agentReady = true;
240
+ agentToken = isLocal ? (liveLocal ?? liveCloud ?? undefined) : (liveCloud ?? liveLocal ?? undefined);
241
+ }
242
+ else {
243
+ agentReady = false;
244
+ agentBlockedReason = globalAuth
245
+ ? 'global user token expired — run `npx ugly-app login`'
246
+ : 'not signed in — run `npx ugly-app login` on the host';
247
+ }
248
+ return { lines, nextSteps, agentReady, agentBlockedReason, agentToken };
249
+ }
250
+ //# sourceMappingURL=probeAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probeAuth.js","sourceRoot":"","sources":["../../src/cli/probeAuth.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/cli/scaffold.ts"],"names":[],"mappings":"AAcA,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuExE"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/cli/scaffold.ts"],"names":[],"mappings":"AAcA,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ExE"}
@@ -42,6 +42,13 @@ export async function scaffoldProject(projectName) {
42
42
  console.log('[ugly-app] Generated server/migrations/001_bootstrap.ts');
43
43
  console.log('[ugly-app] Installing dependencies...');
44
44
  execSync('pnpm install', { cwd: destDir, stdio: 'inherit' });
45
+ console.log('[ugly-app] Installing Playwright Chromium browser...');
46
+ try {
47
+ execSync('npx playwright install chromium', { cwd: destDir, stdio: 'inherit' });
48
+ }
49
+ catch (err) {
50
+ console.warn('[ugly-app] Playwright browser install failed (non-fatal):', err);
51
+ }
45
52
  const localPort = DEFAULT_LOCAL_PORT;
46
53
  // Only alphanumeric — safe for R2 buckets, Postgres usernames, Docker tags
47
54
  const generateId = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789', 10);
@@ -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,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.1.417";
1
+ export declare const CLI_VERSION = "0.1.419";
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.417";
2
+ export const CLI_VERSION = "0.1.419";
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.417",
3
+ "version": "0.1.419",
4
4
  "type": "module",
5
5
  "main": "./dist/server/index.js",
6
6
  "exports": {
@@ -1,6 +1,7 @@
1
1
  import { nanoid } from 'nanoid';
2
2
  import { getUglyBotUrl } from '../shared/uglyBotUrl.js';
3
3
  import { initDataProxy, pgQuery, stopDataProxy, ensureTable } from '../server/DataProxyClient.js';
4
+ import { migrateTokensFromUglyApp, readAuthToken } from './authStore.js';
4
5
 
5
6
  export async function createAccount(
6
7
  email: string,
@@ -50,13 +51,24 @@ export async function createBot(
50
51
  ): Promise<{ userId: string; token: string }> {
51
52
  const fs = await import('fs');
52
53
  let projectToken = '';
54
+ let projectId = '';
53
55
 
54
- // Read from .uglyapp config
56
+ // Migrate any legacy .uglyapp tokens into ~/.ugly-bot/ first.
57
+ migrateTokensFromUglyApp(process.cwd());
58
+
59
+ // Read projectId (and any leftover inline token) from .uglyapp.
55
60
  try {
56
61
  const config = JSON.parse(fs.readFileSync('.uglyapp', 'utf-8')) as Record<string, unknown>;
62
+ if (typeof config.projectId === 'string') projectId = config.projectId;
57
63
  if (typeof config.uglyBotToken === 'string') projectToken = config.uglyBotToken;
58
64
  } catch { /* no .uglyapp */ }
59
65
 
66
+ // Primary source: ~/.ugly-bot/ auth store (where `npx ugly-app login` writes tokens).
67
+ if (!projectToken && projectId) {
68
+ const stored = readAuthToken(projectId);
69
+ if (stored) projectToken = stored;
70
+ }
71
+
60
72
  // Fall back to .env for backwards compatibility
61
73
  if (!projectToken) {
62
74
  try {
@@ -68,7 +80,7 @@ export async function createBot(
68
80
 
69
81
  if (!projectToken) {
70
82
  throw new Error(
71
- 'uglyBotToken not found in .uglyapp. Run "npx ugly-app login" first.',
83
+ 'No project auth token found. Run "npx ugly-app login" in this project to authenticate.',
72
84
  );
73
85
  }
74
86
 
package/src/cli/doctor.ts CHANGED
@@ -10,18 +10,29 @@
10
10
  */
11
11
 
12
12
  import fs from 'fs';
13
+ import os from 'os';
13
14
  import path from 'path';
14
15
  import { readUglyAppConfig } from './uglyappConfig.js';
15
- import { readAuthToken, readLocalToken } from './authStore.js';
16
+ import { probeAuth, type AuthLine } from './probeAuth.js';
16
17
 
17
- interface DoctorLine {
18
- status: 'ok' | 'warn' | 'error' | 'info';
19
- label: string;
20
- detail: string;
18
+ type DoctorLine = AuthLine;
19
+
20
+ export interface RunDoctorOptions {
21
+ /** Override for tests. Defaults to process.cwd(). */
22
+ cwd?: string;
23
+ /** Override for tests. Defaults to os.homedir(). */
24
+ homeDir?: string;
25
+ /** Skip the live DataProxy / migration probes. Defaults to false. */
26
+ skipLiveChecks?: boolean;
27
+ /** Sink for output lines. Defaults to console.log. */
28
+ print?: (line: string) => void;
21
29
  }
22
30
 
23
- export async function runDoctor(): Promise<void> {
24
- const cwd = process.cwd();
31
+ export async function runDoctor(options: RunDoctorOptions = {}): Promise<void> {
32
+ const cwd = options.cwd ?? process.cwd();
33
+ const homeDir = options.homeDir ?? os.homedir();
34
+ const print = options.print ?? ((s: string) => console.log(s));
35
+ const skipLive = options.skipLiveChecks ?? false;
25
36
  const lines: DoctorLine[] = [];
26
37
  const nextSteps: string[] = [];
27
38
 
@@ -42,40 +53,17 @@ export async function runDoctor(): Promise<void> {
42
53
  });
43
54
  }
44
55
 
45
- // 2. Auth tokens
56
+ // 2. Auth tokens — global user identity + per-project cached tokens.
57
+ // probeAuth() is the single source of truth so test code can exercise
58
+ // the same logic without spinning up a DataProxy.
46
59
  const isLocal = process.env['UGLY_BOT_LOCAL'] === '1';
47
- if (config) {
48
- const cloudToken = readAuthToken(config.projectId) ?? config.uglyBotToken;
49
- const localToken = readLocalToken(config.projectId) ?? config.localToken;
50
- if (cloudToken) {
51
- lines.push({
52
- status: 'ok',
53
- label: 'auth (cloud)',
54
- detail: `project token present (${cloudToken.slice(0, 8)}…)`,
55
- });
56
- } else {
57
- lines.push({
58
- status: isLocal ? 'info' : 'warn',
59
- label: 'auth (cloud)',
60
- detail: 'no project token for ugly.bot cloud',
61
- });
62
- if (!isLocal) nextSteps.push('Run `npx ugly-app login` to mint a project token');
63
- }
64
- if (localToken) {
65
- lines.push({
66
- status: 'ok',
67
- label: 'auth (local)',
68
- detail: `local project token present (${localToken.slice(0, 8)}…)`,
69
- });
70
- } else {
71
- lines.push({
72
- status: isLocal ? 'warn' : 'info',
73
- label: 'auth (local)',
74
- detail: 'no local ugly.bot token',
75
- });
76
- if (isLocal) nextSteps.push('Run `UGLY_BOT_LOCAL=1 npx ugly-app login` to mint a local token');
77
- }
78
- }
60
+ const authProbe = probeAuth({
61
+ homeDir,
62
+ projectId: config?.projectId ?? null,
63
+ isLocal,
64
+ });
65
+ for (const l of authProbe.lines) lines.push(l);
66
+ for (const s of authProbe.nextSteps) nextSteps.push(s);
79
67
 
80
68
  // 3. DataProxy / env inheritance (pre-ensureInfra snapshot)
81
69
  const envUrl = process.env['DATA_PROXY_URL'];
@@ -122,7 +110,7 @@ export async function runDoctor(): Promise<void> {
122
110
  // 5. Live DataProxy connection check (only when .uglyapp + some token).
123
111
  // Gated on config presence so doctor is fast in a half-configured app
124
112
  // instead of blocking on a tunnel handshake that can't succeed.
125
- if (config) {
113
+ if (config && !skipLive) {
126
114
  const connectCheck = await safeConnectCheck(cwd);
127
115
  lines.push({
128
116
  status: connectCheck.ok ? 'ok' : 'error',
@@ -146,8 +134,8 @@ export async function runDoctor(): Promise<void> {
146
134
  }
147
135
 
148
136
  // Print the report
149
- console.log('ugly-app doctor — child app diagnostics');
150
- console.log('═'.repeat(50));
137
+ print('ugly-app doctor — child app diagnostics');
138
+ print('═'.repeat(50));
151
139
  for (const l of lines) {
152
140
  const icon =
153
141
  l.status === 'ok'
@@ -157,15 +145,26 @@ export async function runDoctor(): Promise<void> {
157
145
  : l.status === 'error'
158
146
  ? '✗'
159
147
  : '·';
160
- console.log(`${icon} ${l.label.padEnd(18)} ${l.detail}`);
148
+ print(`${icon} ${l.label.padEnd(18)} ${l.detail}`);
161
149
  }
150
+
151
+ // Agent-ready summary — a single grep-friendly line so headless
152
+ // tooling (the bot-swarm skill, evals, CI) can decide whether to
153
+ // proceed without parsing the human-formatted lines above.
154
+ print('');
155
+ if (authProbe.agentReady) {
156
+ print('agent-ready: yes');
157
+ } else {
158
+ print(`agent-ready: no — ${authProbe.agentBlockedReason ?? 'auth incomplete'}`);
159
+ }
160
+
162
161
  if (nextSteps.length > 0) {
163
- console.log('');
164
- console.log('Next steps:');
165
- for (const s of nextSteps) console.log(` • ${s}`);
162
+ print('');
163
+ print('Next steps:');
164
+ for (const s of nextSteps) print(` • ${s}`);
166
165
  } else {
167
- console.log('');
168
- console.log('Next steps: nothing obvious — env looks healthy.');
166
+ print('');
167
+ print('Next steps: nothing obvious — env looks healthy.');
169
168
  }
170
169
  }
171
170
 
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Pure auth-state probe used by `ugly-app doctor` (and by tests).
3
+ *
4
+ * Reads the on-disk auth files under a given home directory and returns
5
+ * a structured report. No side effects, no DataProxy, no network.
6
+ *
7
+ * ~/.ugly-bot/auth.json — global user identity ({ token, userId, serverUrl })
8
+ * ~/.ugly-bot/<projectId>.json — cloud project token ({ token })
9
+ * ~/.ugly-bot/<projectId>.local.json — local project token ({ token })
10
+ *
11
+ * The doctor previously only checked the project-level files and
12
+ * reported "no project token for cloud" as a warning even when a valid
13
+ * global user identity existed (in which case `dev` / `auth:create-bot`
14
+ * will mint a project token automatically on first run). Headless
15
+ * agents reading the report concluded auth was broken and got stuck on
16
+ * `npx ugly-app login`, which needs a browser.
17
+ */
18
+ import fs from 'node:fs';
19
+ import path from 'node:path';
20
+
21
+ export type AuthStatus = 'ok' | 'warn' | 'error' | 'info';
22
+
23
+ export interface AuthLine {
24
+ status: AuthStatus;
25
+ label: string;
26
+ detail: string;
27
+ }
28
+
29
+ export interface ProbeAuthInput {
30
+ homeDir: string;
31
+ projectId: string | null;
32
+ /** True when running against localhost ugly.bot (UGLY_BOT_LOCAL=1). */
33
+ isLocal: boolean;
34
+ }
35
+
36
+ export interface ProbeAuthResult {
37
+ /** Human-readable lines, in the order they should be printed. */
38
+ lines: AuthLine[];
39
+ /** Hints to show under "Next steps:". */
40
+ nextSteps: string[];
41
+ /**
42
+ * True when the on-disk state is sufficient for a headless agent to
43
+ * proceed: at minimum a non-expired global user token exists, OR a
44
+ * non-expired project token of the right kind (cloud/local) exists.
45
+ * Project tokens are auto-minted from the global user token on first
46
+ * run, so the global user token is the real prerequisite.
47
+ */
48
+ agentReady: boolean;
49
+ /** When agentReady is false, a one-line reason. */
50
+ agentBlockedReason?: string;
51
+ /**
52
+ * The token an agent should put in `UGLY_BOT_TOKEN`, if one is
53
+ * available. Prefers the project token (matches what the framework
54
+ * uses at runtime); falls back to the global user token.
55
+ */
56
+ agentToken?: string;
57
+ }
58
+
59
+ interface JsonReadResult {
60
+ data: Record<string, unknown> | null;
61
+ /** Set when the file exists but couldn't be parsed — distinct from "missing". */
62
+ malformed: boolean;
63
+ }
64
+
65
+ function readJson(filePath: string): JsonReadResult {
66
+ try {
67
+ const raw = fs.readFileSync(filePath, 'utf-8');
68
+ try {
69
+ const parsed = JSON.parse(raw) as Record<string, unknown>;
70
+ if (parsed && typeof parsed === 'object') {
71
+ return { data: parsed, malformed: false };
72
+ }
73
+ return { data: null, malformed: true };
74
+ } catch {
75
+ return { data: null, malformed: true };
76
+ }
77
+ } catch {
78
+ return { data: null, malformed: false };
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Inspects the JWT exp claim without verifying the signature. Returns:
84
+ * - 'valid' : exp present and at least 60s in the future
85
+ * - 'expired' : exp present and in the past (or within 60s)
86
+ * - 'unknown' : token doesn't have a parseable exp — treat as valid
87
+ * since some service tokens are intentionally non-expiring
88
+ */
89
+ export function inspectJwt(token: string): 'valid' | 'expired' | 'unknown' {
90
+ const parts = token.split('.');
91
+ if (parts.length !== 3) return 'unknown';
92
+ try {
93
+ const payload = JSON.parse(
94
+ Buffer.from(parts[1], 'base64url').toString('utf-8'),
95
+ ) as { exp?: number };
96
+ if (typeof payload.exp !== 'number') return 'unknown';
97
+ return Date.now() / 1000 < payload.exp - 60 ? 'valid' : 'expired';
98
+ } catch {
99
+ return 'unknown';
100
+ }
101
+ }
102
+
103
+ interface GlobalAuth {
104
+ token: string;
105
+ userId: string;
106
+ serverUrl: string;
107
+ }
108
+
109
+ function readGlobalAuth(homeDir: string): {
110
+ auth: GlobalAuth | null;
111
+ malformed: boolean;
112
+ } {
113
+ const file = path.join(homeDir, '.ugly-bot', 'auth.json');
114
+ const { data, malformed } = readJson(file);
115
+ if (!data) return { auth: null, malformed };
116
+ if (
117
+ typeof data.token !== 'string' ||
118
+ typeof data.userId !== 'string' ||
119
+ typeof data.serverUrl !== 'string'
120
+ ) {
121
+ return { auth: null, malformed: true };
122
+ }
123
+ return {
124
+ auth: { token: data.token, userId: data.userId, serverUrl: data.serverUrl },
125
+ malformed: false,
126
+ };
127
+ }
128
+
129
+ function readProjectToken(
130
+ homeDir: string,
131
+ projectId: string,
132
+ mode: 'cloud' | 'local',
133
+ ): { token: string | null; malformed: boolean } {
134
+ const suffix = mode === 'local' ? '.local.json' : '.json';
135
+ const file = path.join(homeDir, '.ugly-bot', `${projectId}${suffix}`);
136
+ const { data, malformed } = readJson(file);
137
+ if (!data) return { token: null, malformed };
138
+ if (typeof data.token !== 'string') return { token: null, malformed: true };
139
+ return { token: data.token, malformed: false };
140
+ }
141
+
142
+ export function probeAuth(input: ProbeAuthInput): ProbeAuthResult {
143
+ const { homeDir, projectId, isLocal } = input;
144
+ const lines: AuthLine[] = [];
145
+ const nextSteps: string[] = [];
146
+
147
+ // 1. Global user identity (~/.ugly-bot/auth.json).
148
+ const { auth: globalAuth, malformed: globalMalformed } = readGlobalAuth(homeDir);
149
+ let globalValid = false;
150
+ if (globalAuth) {
151
+ const exp = inspectJwt(globalAuth.token);
152
+ if (exp === 'expired') {
153
+ lines.push({
154
+ status: 'warn',
155
+ label: 'auth (user)',
156
+ detail: `token expired (userId=${globalAuth.userId})`,
157
+ });
158
+ nextSteps.push('Run `npx ugly-app login` to refresh your global user token');
159
+ } else {
160
+ globalValid = true;
161
+ lines.push({
162
+ status: 'ok',
163
+ label: 'auth (user)',
164
+ detail: `signed in as ${globalAuth.userId} (${globalAuth.serverUrl})`,
165
+ });
166
+ }
167
+ } else if (globalMalformed) {
168
+ lines.push({
169
+ status: 'error',
170
+ label: 'auth (user)',
171
+ detail: '~/.ugly-bot/auth.json is malformed or missing required fields',
172
+ });
173
+ nextSteps.push(
174
+ 'Delete ~/.ugly-bot/auth.json and re-run `npx ugly-app login`',
175
+ );
176
+ } else {
177
+ lines.push({
178
+ status: 'error',
179
+ label: 'auth (user)',
180
+ detail: 'not signed in (no ~/.ugly-bot/auth.json)',
181
+ });
182
+ nextSteps.push(
183
+ 'Run `npx ugly-app login` once on the host (interactive, opens a browser) — required even for headless agents',
184
+ );
185
+ }
186
+
187
+ // 2. Project tokens. Without a .uglyapp we have no projectId to look up.
188
+ let cloudToken: string | null = null;
189
+ let cloudExpired = false;
190
+ let localToken: string | null = null;
191
+ let localExpired = false;
192
+
193
+ if (projectId) {
194
+ const cloud = readProjectToken(homeDir, projectId, 'cloud');
195
+ const local = readProjectToken(homeDir, projectId, 'local');
196
+
197
+ if (cloud.token) {
198
+ const exp = inspectJwt(cloud.token);
199
+ if (exp === 'expired') {
200
+ cloudExpired = true;
201
+ lines.push({
202
+ status: 'warn',
203
+ label: 'auth (cloud)',
204
+ detail: `cached project token expired (${cloud.token.slice(0, 8)}…)`,
205
+ });
206
+ if (!isLocal && globalValid) {
207
+ nextSteps.push(
208
+ 'Stale cloud project token — `ugly-app dev` will re-mint it from your user token',
209
+ );
210
+ }
211
+ } else {
212
+ cloudToken = cloud.token;
213
+ lines.push({
214
+ status: 'ok',
215
+ label: 'auth (cloud)',
216
+ detail: `project token present (${cloud.token.slice(0, 8)}…)`,
217
+ });
218
+ }
219
+ } else if (cloud.malformed) {
220
+ lines.push({
221
+ status: 'warn',
222
+ label: 'auth (cloud)',
223
+ detail: `~/.ugly-bot/${projectId}.json is malformed`,
224
+ });
225
+ nextSteps.push(`Delete ~/.ugly-bot/${projectId}.json and re-run \`npx ugly-app dev\` to re-mint`);
226
+ } else if (globalValid && !isLocal) {
227
+ // Common case: never run before. Don't scare the agent — it's auto-minted.
228
+ lines.push({
229
+ status: 'info',
230
+ label: 'auth (cloud)',
231
+ detail: 'no cached project token — will be minted from user token on first run',
232
+ });
233
+ } else {
234
+ lines.push({
235
+ status: isLocal ? 'info' : 'warn',
236
+ label: 'auth (cloud)',
237
+ detail: 'no project token for ugly.bot cloud',
238
+ });
239
+ if (!isLocal && !globalValid) {
240
+ // The "log in first" hint already came from the user line; don't duplicate.
241
+ }
242
+ }
243
+
244
+ if (local.token) {
245
+ const exp = inspectJwt(local.token);
246
+ if (exp === 'expired') {
247
+ localExpired = true;
248
+ lines.push({
249
+ status: isLocal ? 'warn' : 'info',
250
+ label: 'auth (local)',
251
+ detail: `cached local token expired (${local.token.slice(0, 8)}…)`,
252
+ });
253
+ if (isLocal) {
254
+ nextSteps.push(
255
+ 'Stale local project token — `UGLY_BOT_LOCAL=1 npx ugly-app dev` will re-mint it',
256
+ );
257
+ }
258
+ } else {
259
+ localToken = local.token;
260
+ lines.push({
261
+ status: 'ok',
262
+ label: 'auth (local)',
263
+ detail: `local project token present (${local.token.slice(0, 8)}…)`,
264
+ });
265
+ }
266
+ } else if (local.malformed) {
267
+ lines.push({
268
+ status: 'warn',
269
+ label: 'auth (local)',
270
+ detail: `~/.ugly-bot/${projectId}.local.json is malformed`,
271
+ });
272
+ } else {
273
+ lines.push({
274
+ status: isLocal ? (globalValid ? 'info' : 'warn') : 'info',
275
+ label: 'auth (local)',
276
+ detail: isLocal && globalValid
277
+ ? 'no cached local project token — will be minted on first local run'
278
+ : 'no local ugly.bot token',
279
+ });
280
+ }
281
+ }
282
+
283
+ // 3. Agent-ready summary.
284
+ // For headless tooling, the global user token is what unblocks
285
+ // everything: project tokens are derived. If we're in local mode we
286
+ // still want the global token because the local project token is
287
+ // minted from it.
288
+ let agentReady = false;
289
+ let agentBlockedReason: string | undefined;
290
+ let agentToken: string | undefined;
291
+
292
+ const liveCloud = cloudToken && !cloudExpired ? cloudToken : null;
293
+ const liveLocal = localToken && !localExpired ? localToken : null;
294
+
295
+ if (globalValid) {
296
+ agentReady = true;
297
+ agentToken = isLocal ? (liveLocal ?? globalAuth?.token) : (liveCloud ?? globalAuth?.token);
298
+ } else if (liveCloud || liveLocal) {
299
+ // Edge case: project tokens cached but global identity gone. The
300
+ // project tokens still work for `auth:create-bot` etc., so don't
301
+ // block, but flag it.
302
+ agentReady = true;
303
+ agentToken = isLocal ? (liveLocal ?? liveCloud ?? undefined) : (liveCloud ?? liveLocal ?? undefined);
304
+ } else {
305
+ agentReady = false;
306
+ agentBlockedReason = globalAuth
307
+ ? 'global user token expired — run `npx ugly-app login`'
308
+ : 'not signed in — run `npx ugly-app login` on the host';
309
+ }
310
+
311
+ return { lines, nextSteps, agentReady, agentBlockedReason, agentToken };
312
+ }
@@ -56,6 +56,13 @@ export async function scaffoldProject(projectName: string): Promise<void> {
56
56
  console.log('[ugly-app] Installing dependencies...');
57
57
  execSync('pnpm install', { cwd: destDir, stdio: 'inherit' });
58
58
 
59
+ console.log('[ugly-app] Installing Playwright Chromium browser...');
60
+ try {
61
+ execSync('npx playwright install chromium', { cwd: destDir, stdio: 'inherit' });
62
+ } catch (err) {
63
+ console.warn('[ugly-app] Playwright browser install failed (non-fatal):', err);
64
+ }
65
+
59
66
  const localPort = DEFAULT_LOCAL_PORT;
60
67
  // Only alphanumeric — safe for R2 buckets, Postgres usernames, Docker tags
61
68
  const generateId = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789', 10);
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by prebuild — do not edit manually
2
- export const CLI_VERSION = "0.1.417";
2
+ export const CLI_VERSION = "0.1.419";
@@ -38,7 +38,21 @@ Run `npx ugly-app url` to get the local server URL. Use this value everywhere (b
38
38
 
39
39
  - The dev server must be running **without** `--watch` (see below)
40
40
 
41
- - `.env` must have `UGLY_BOT_TOKEN` (from `npx ugly-app login`)
41
+ - Auth must be set up. **Always run `npx ugly-app doctor` first** to verify. The bottom of its output prints a single line:
42
+
43
+ ```
44
+ agent-ready: yes
45
+ ```
46
+
47
+ If it says `agent-ready: no — ...`, follow the reason verbatim. The most common cause is no global user token on the host — the user must run `npx ugly-app login` once interactively (it opens a browser and is **not** runnable from within an unattended agent). Do **not** retry `ugly-app login` from inside the cycle; it will deadlock waiting for browser auth.
48
+
49
+ Once `agent-ready: yes`, source the token for downstream `auth:create-bot` / `feedback:submit` calls from `~/.ugly-bot/auth.json`:
50
+
51
+ ```bash
52
+ export UGLY_BOT_TOKEN="$(node -e 'console.log(JSON.parse(require("fs").readFileSync(require("os").homedir()+"/.ugly-bot/auth.json","utf8")).token)')"
53
+ ```
54
+
55
+ Project-scoped tokens under `~/.ugly-bot/<projectId>.json` are minted automatically by `ugly-app dev` from the global token — do not try to mint them manually.
42
56
 
43
57
  - Playwright must be installed (`npx playwright install chromium`)
44
58
 
@@ -57,6 +57,7 @@
57
57
  "eslint-plugin-react": "^7.37.0",
58
58
  "eslint-plugin-react-hooks": "^5.0.0",
59
59
  "html2canvas": "^1.4.1",
60
+ "playwright": "^1.58.2",
60
61
  "tsx": "^4.19.0",
61
62
  "typescript": "^5.9.3",
62
63
  "typescript-eslint": "^8.0.0",