@notis_ai/cli 0.2.0-beta.92.1 → 0.2.0-beta.99.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/README.md +20 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-notes/app/page.tsx +313 -175
- package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
- package/package.json +1 -1
- package/skills/notis-apps/SKILL.md +25 -5
- package/skills/notis-apps/cli.md +20 -0
- package/skills/notis-cli/SKILL.md +9 -0
- package/src/command-specs/apps.js +249 -7
- package/src/command-specs/meta.js +8 -2
- package/src/runtime/agent-browser.js +89 -0
- package/src/runtime/app-dev-server.js +92 -2
- package/src/runtime/app-dev-sessions.js +77 -11
- package/src/runtime/app-platform.js +5 -23
- package/src/runtime/desktop-auth.js +93 -0
- package/src/runtime/profiles.js +37 -15
- package/src/runtime/transport.js +21 -82
- package/dist/scaffolds/notis-database/metadata/cover.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/cover.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-random/metadata/cover.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-3.png +0 -0
- package/template/metadata/cover.png +0 -0
package/src/runtime/transport.js
CHANGED
|
@@ -3,11 +3,11 @@ import { createReadStream } from 'node:fs';
|
|
|
3
3
|
import { CliError, EXIT_CODES } from './errors.js';
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_PROFILE,
|
|
6
|
-
ensureProfile,
|
|
7
6
|
getProfile,
|
|
7
|
+
isJwtExpired,
|
|
8
8
|
loadConfig,
|
|
9
|
-
saveConfig,
|
|
10
9
|
} from './profiles.js';
|
|
10
|
+
import { createExpiredAuthError, createInvalidAuthHints } from './desktop-auth.js';
|
|
11
11
|
|
|
12
12
|
function escapeMultipartHeaderValue(value) {
|
|
13
13
|
return String(value ?? '')
|
|
@@ -76,7 +76,7 @@ function createMultipartFileUpload(payload, bindingMetadata, fileBindings) {
|
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
function normalizeBackendError(status, payload) {
|
|
79
|
+
function normalizeBackendError(status, payload, runtime) {
|
|
80
80
|
const backendError = payload?.error;
|
|
81
81
|
const message =
|
|
82
82
|
backendError?.message ||
|
|
@@ -91,10 +91,7 @@ function normalizeBackendError(status, payload) {
|
|
|
91
91
|
message,
|
|
92
92
|
exitCode: EXIT_CODES.auth,
|
|
93
93
|
hints: [
|
|
94
|
-
|
|
95
|
-
command: 'Open the Notis desktop app and sign in',
|
|
96
|
-
reason: 'Open the Notis desktop app to refresh CLI auth, or set NOTIS_JWT',
|
|
97
|
-
},
|
|
94
|
+
...createInvalidAuthHints(runtime),
|
|
98
95
|
],
|
|
99
96
|
details: payload || {},
|
|
100
97
|
});
|
|
@@ -145,46 +142,10 @@ function normalizeBackendError(status, payload) {
|
|
|
145
142
|
});
|
|
146
143
|
}
|
|
147
144
|
|
|
148
|
-
function
|
|
149
|
-
if (runtime.
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (force) {
|
|
154
|
-
return true;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (typeof runtime.accessExpiresAt !== 'number' || runtime.accessExpiresAt <= 0) {
|
|
145
|
+
function reloadJwtFromConfig(runtime) {
|
|
146
|
+
if (runtime.credentialSource === 'env') {
|
|
158
147
|
return false;
|
|
159
148
|
}
|
|
160
|
-
|
|
161
|
-
const thresholdSeconds = 60;
|
|
162
|
-
return runtime.accessExpiresAt - Math.floor(Date.now() / 1000) <= thresholdSeconds;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function persistDevPortalRuntimeAuth(runtime, session) {
|
|
166
|
-
const config = ensureProfile(loadConfig(), runtime.profileName || DEFAULT_PROFILE);
|
|
167
|
-
const profileName = runtime.profileName || DEFAULT_PROFILE;
|
|
168
|
-
config.current_profile = profileName;
|
|
169
|
-
config.profiles[profileName] = {
|
|
170
|
-
...config.profiles[profileName],
|
|
171
|
-
jwt: session.access_token,
|
|
172
|
-
api_base: runtime.apiBase,
|
|
173
|
-
auth_mode: 'dev_portal',
|
|
174
|
-
refresh_token: session.refresh_token,
|
|
175
|
-
access_expires_at: session.access_expires_at,
|
|
176
|
-
refresh_expires_at: session.refresh_expires_at,
|
|
177
|
-
};
|
|
178
|
-
saveConfig(config);
|
|
179
|
-
|
|
180
|
-
runtime.jwt = session.access_token;
|
|
181
|
-
runtime.authMode = 'dev_portal';
|
|
182
|
-
runtime.refreshToken = session.refresh_token;
|
|
183
|
-
runtime.accessExpiresAt = session.access_expires_at;
|
|
184
|
-
runtime.refreshExpiresAt = session.refresh_expires_at;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function reloadJwtFromConfig(runtime) {
|
|
188
149
|
const profileName = runtime.profileName || DEFAULT_PROFILE;
|
|
189
150
|
let profile;
|
|
190
151
|
try {
|
|
@@ -197,39 +158,8 @@ function reloadJwtFromConfig(runtime) {
|
|
|
197
158
|
return false;
|
|
198
159
|
}
|
|
199
160
|
runtime.jwt = nextJwt;
|
|
200
|
-
runtime.
|
|
201
|
-
runtime.
|
|
202
|
-
runtime.accessExpiresAt = profile.access_expires_at;
|
|
203
|
-
runtime.refreshExpiresAt = profile.refresh_expires_at;
|
|
204
|
-
return true;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
async function maybeRefreshDevPortalAuth(runtime, { force = false } = {}) {
|
|
208
|
-
if (!shouldAttemptDevPortalRefresh(runtime, { force })) {
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const response = await fetch(`${runtime.apiBase}/portal_auth/dev-refresh`, {
|
|
213
|
-
method: 'POST',
|
|
214
|
-
headers: {
|
|
215
|
-
'Content-Type': 'application/json',
|
|
216
|
-
'X-Notis-CLI-Version': runtime.cliVersion,
|
|
217
|
-
},
|
|
218
|
-
body: JSON.stringify({ refresh_token: runtime.refreshToken }),
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
let payload = null;
|
|
222
|
-
try {
|
|
223
|
-
payload = await response.json();
|
|
224
|
-
} catch {
|
|
225
|
-
payload = null;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (!response.ok || !payload?.session?.access_token || !payload?.session?.refresh_token) {
|
|
229
|
-
throw normalizeBackendError(response.status, payload);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
persistDevPortalRuntimeAuth(runtime, payload.session);
|
|
161
|
+
runtime.desktopAppName = profile.desktop_app_name;
|
|
162
|
+
runtime.desktopPid = profile.desktop_pid;
|
|
233
163
|
return true;
|
|
234
164
|
}
|
|
235
165
|
|
|
@@ -241,7 +171,14 @@ export async function httpRequest({
|
|
|
241
171
|
multipart = false,
|
|
242
172
|
requireAuth = true,
|
|
243
173
|
}) {
|
|
244
|
-
|
|
174
|
+
// The desktop renderer is the single owner of the rotating Supabase refresh
|
|
175
|
+
// token. Each CLI request only consumes the newest access token it synced to
|
|
176
|
+
// disk, avoiding refresh-token races between independent CLI processes and
|
|
177
|
+
// the running desktop session.
|
|
178
|
+
reloadJwtFromConfig(runtime);
|
|
179
|
+
if (requireAuth && isJwtExpired(runtime.jwt)) {
|
|
180
|
+
throw createExpiredAuthError(runtime);
|
|
181
|
+
}
|
|
245
182
|
|
|
246
183
|
let controller = new AbortController();
|
|
247
184
|
let timeout = setTimeout(() => controller.abort(), runtime.timeoutMs);
|
|
@@ -298,8 +235,7 @@ export async function httpRequest({
|
|
|
298
235
|
}
|
|
299
236
|
|
|
300
237
|
if (response.status === 401) {
|
|
301
|
-
const refreshed = (
|
|
302
|
-
|| reloadJwtFromConfig(runtime);
|
|
238
|
+
const refreshed = reloadJwtFromConfig(runtime) && !isJwtExpired(runtime.jwt);
|
|
303
239
|
if (refreshed) {
|
|
304
240
|
if (requireAuth && runtime.jwt) {
|
|
305
241
|
headers.Authorization = `Bearer ${runtime.jwt}`;
|
|
@@ -324,7 +260,10 @@ export async function httpRequest({
|
|
|
324
260
|
clearTimeout(timeout);
|
|
325
261
|
|
|
326
262
|
if (!response.ok) {
|
|
327
|
-
|
|
263
|
+
if (response.status === 401 && isJwtExpired(runtime.jwt)) {
|
|
264
|
+
throw createExpiredAuthError(runtime);
|
|
265
|
+
}
|
|
266
|
+
throw normalizeBackendError(response.status, payload, runtime);
|
|
328
267
|
}
|
|
329
268
|
|
|
330
269
|
return {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|