atris 3.58.5 → 3.58.7
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 +8 -0
- package/atris/policies/engineering-principles.md +129 -0
- package/atris/policies/genesis.md +112 -0
- package/atris/policies/product-design-principles.md +100 -0
- package/atris/skills/design/SKILL.md +3 -1
- package/atris/skills/engines/SKILL.md +3 -3
- package/atris/skills/x-search/SKILL.md +2 -2
- package/atris/skills/youtube/SKILL.md +44 -28
- package/bin/atris.js +56 -3
- package/commands/auth.js +58 -24
- package/commands/brain.js +1 -0
- package/commands/design.js +362 -0
- package/commands/doc-health.js +329 -0
- package/commands/drive.js +32 -0
- package/commands/improve.js +67 -1
- package/commands/land.js +144 -4
- package/commands/learn.js +211 -40
- package/commands/member.js +65 -11
- package/commands/mission.js +37 -7
- package/commands/pulse.js +38 -0
- package/commands/rsi.js +156 -0
- package/commands/task.js +41 -1
- package/commands/workflow.js +15 -14
- package/commands/x-search.js +9 -10
- package/commands/youtube.js +518 -107
- package/lib/apply-gate.js +22 -4
- package/lib/daily-log.js +88 -0
- package/lib/design-api.js +130 -0
- package/lib/engine-ask.js +1 -1
- package/lib/first-minute.js +1 -6
- package/lib/known-commands.js +3 -3
- package/lib/member-context.js +42 -0
- package/lib/rsi-record.js +335 -0
- package/lib/state-detection.js +8 -8
- package/lib/task-db.js +71 -51
- package/lib/task-list-keeper.js +192 -0
- package/lib/todo-fallback.js +9 -3
- package/lib/todo.js +22 -10
- package/mcp/atris-mcp/index.mjs +174 -0
- package/package.json +8 -3
- package/scripts/det/ytnotes +122 -10
- package/utils/auth.js +109 -13
package/utils/auth.js
CHANGED
|
@@ -202,6 +202,51 @@ function loadPlacedAgentToken() {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
function leftoverStoredAgentToken(parsed, envToken) {
|
|
206
|
+
if (!parsed || !envToken) return null;
|
|
207
|
+
const leftover = typeof parsed.agent_token === 'string' ? parsed.agent_token.trim() : '';
|
|
208
|
+
if (!leftover || leftover !== envToken) return null;
|
|
209
|
+
const expiresAt = parsed.agent_token_expires_at;
|
|
210
|
+
if (expiryTimeMs(expiresAt) === null) return null;
|
|
211
|
+
const scopes = Array.isArray(parsed.agent_token_scopes) ? parsed.agent_token_scopes : [];
|
|
212
|
+
return {
|
|
213
|
+
token: leftover,
|
|
214
|
+
expires_at: expiresAt,
|
|
215
|
+
scopes,
|
|
216
|
+
provider: null,
|
|
217
|
+
source: 'env',
|
|
218
|
+
agent_token: leftover,
|
|
219
|
+
agent_token_scopes: scopes,
|
|
220
|
+
agent_token_expires_at: expiresAt,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function loadLeftoverCredentialsAgentToken(envToken) {
|
|
225
|
+
if (!envToken) return null;
|
|
226
|
+
try {
|
|
227
|
+
return leftoverStoredAgentToken(JSON.parse(fs.readFileSync(getCredentialsPath(), 'utf8')), envToken);
|
|
228
|
+
} catch {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function resolveProfileOverride(profileOverride) {
|
|
234
|
+
if (!profileOverride) return null;
|
|
235
|
+
if (loadProfile(profileOverride)) return profileOverride;
|
|
236
|
+
const profiles = listProfiles();
|
|
237
|
+
const q = String(profileOverride).toLowerCase();
|
|
238
|
+
return profiles.find((name) => name.toLowerCase() === q)
|
|
239
|
+
|| profiles.find((name) => name.toLowerCase().startsWith(q))
|
|
240
|
+
|| profiles.find((name) => name.toLowerCase().includes(q))
|
|
241
|
+
|| null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function loadLeftoverProfileAgentToken(envToken) {
|
|
245
|
+
const name = resolveProfileOverride(process.env.ATRIS_PROFILE) || getSessionProfile();
|
|
246
|
+
if (!envToken || !name) return null;
|
|
247
|
+
return leftoverStoredAgentToken(loadProfile(name), envToken);
|
|
248
|
+
}
|
|
249
|
+
|
|
205
250
|
function isUnexpiredCredential(credentials) {
|
|
206
251
|
const expiresAt = expiryTimeMs(credentials?.expires_at);
|
|
207
252
|
return expiresAt !== null && expiresAt > Date.now();
|
|
@@ -352,9 +397,13 @@ function autoSaveProfile(credentials) {
|
|
|
352
397
|
}
|
|
353
398
|
}
|
|
354
399
|
|
|
355
|
-
function saveCredentials(token, refreshToken, email, userId, provider) {
|
|
400
|
+
function saveCredentials(token, refreshToken, email, userId, provider, extras = {}) {
|
|
401
|
+
if (decodeJwtClaims(token)?.type === 'agent_access') {
|
|
402
|
+
throw new Error('Refusing to save a scoped agent token as the login token; keep it under agent_token');
|
|
403
|
+
}
|
|
356
404
|
const credentialsPath = getCredentialsPath();
|
|
357
405
|
const credentials = {
|
|
406
|
+
...extras,
|
|
358
407
|
token,
|
|
359
408
|
refresh_token: refreshToken || null,
|
|
360
409
|
email: email || null,
|
|
@@ -374,10 +423,47 @@ function saveCredentials(token, refreshToken, email, userId, provider) {
|
|
|
374
423
|
autoSaveProfile(credentials);
|
|
375
424
|
}
|
|
376
425
|
|
|
377
|
-
|
|
426
|
+
// Supplying the auth client enables asynchronous repair. Local-only readers stay synchronous.
|
|
427
|
+
function loadCredentials(apiRequestJson) {
|
|
428
|
+
const credentials = readCredentials();
|
|
429
|
+
if (!credentials || credentials.source || decodeJwtClaims(credentials.token)?.type !== 'agent_access') {
|
|
430
|
+
return credentials;
|
|
431
|
+
}
|
|
432
|
+
if (!credentials.refresh_token) {
|
|
433
|
+
console.error('atris login');
|
|
434
|
+
return credentials;
|
|
435
|
+
}
|
|
436
|
+
if (!apiRequestJson) return credentials;
|
|
437
|
+
return repairLoginCredentials(credentials, apiRequestJson);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async function repairLoginCredentials(credentials, apiRequestJson) {
|
|
441
|
+
const refreshed = await refreshAccessToken(credentials.refresh_token, null, apiRequestJson);
|
|
442
|
+
const token = refreshed.data?.access_token;
|
|
443
|
+
if (!refreshed.ok || !token || decodeJwtClaims(token)?.type === 'agent_access') {
|
|
444
|
+
throw new Error('Could not repair login file. Run atris login');
|
|
445
|
+
}
|
|
446
|
+
const claims = decodeJwtClaims(credentials.token);
|
|
447
|
+
const { source_profile, ...rest } = credentials;
|
|
448
|
+
const next = {
|
|
449
|
+
...rest,
|
|
450
|
+
token,
|
|
451
|
+
refresh_token: refreshed.data.refresh_token || credentials.refresh_token,
|
|
452
|
+
agent_token: credentials.token,
|
|
453
|
+
agent_token_scopes: claims.scopes || [],
|
|
454
|
+
agent_token_expires_at: typeof claims.exp === 'number' ? new Date(claims.exp * 1000).toISOString() : null,
|
|
455
|
+
};
|
|
456
|
+
if (source_profile) saveProfile(source_profile, next);
|
|
457
|
+
else saveCredentials(token, next.refresh_token, next.email, next.user_id, next.provider, next);
|
|
458
|
+
console.error('Repaired login file: moved a scoped agent token aside.');
|
|
459
|
+
return { ...next, ...(source_profile ? { source_profile } : {}) };
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function readCredentials() {
|
|
378
463
|
// Priority: ATRIS_TOKEN env var → placed agent token → ATRIS_PROFILE env var
|
|
379
464
|
// → per-terminal session file → global credentials.json. A fresh placed token
|
|
380
|
-
// overrides
|
|
465
|
+
// overrides env when the leftover is newer or when env only repeats that
|
|
466
|
+
// leftover, so billed auth still sees leftover scopes.
|
|
381
467
|
|
|
382
468
|
// 0. Raw token injection. Headless boxes (cloud business computers) have no
|
|
383
469
|
// browser for `atris login`; the runner injects a scoped token as env
|
|
@@ -385,15 +471,24 @@ function loadCredentials() {
|
|
|
385
471
|
const envToken = process.env.ATRIS_TOKEN;
|
|
386
472
|
const normalizedEnvToken = envToken && envToken.trim();
|
|
387
473
|
const placedAgentToken = loadPlacedAgentToken();
|
|
388
|
-
if (
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
placedAgentToken.token !== normalizedEnvToken &&
|
|
392
|
-
isUnexpiredCredential(placedAgentToken)
|
|
393
|
-
) {
|
|
474
|
+
if (normalizedEnvToken && placedAgentToken && isUnexpiredCredential(placedAgentToken)) {
|
|
475
|
+
// Fresh placed leftover wins over env, including when env repeats the same
|
|
476
|
+
// token, so billed auth still sees leftover scopes and does not remint.
|
|
394
477
|
return placedAgentToken;
|
|
395
478
|
}
|
|
396
479
|
if (normalizedEnvToken) {
|
|
480
|
+
const leftoverCredentials = loadLeftoverCredentialsAgentToken(normalizedEnvToken);
|
|
481
|
+
if (leftoverCredentials && isUnexpiredCredential(leftoverCredentials)) {
|
|
482
|
+
// Env repeating leftover credentials.agent_token keeps leftover scopes
|
|
483
|
+
// and expiry so billed auth does not remint.
|
|
484
|
+
return leftoverCredentials;
|
|
485
|
+
}
|
|
486
|
+
const leftoverProfile = loadLeftoverProfileAgentToken(normalizedEnvToken);
|
|
487
|
+
if (leftoverProfile && isUnexpiredCredential(leftoverProfile)) {
|
|
488
|
+
// Env repeating leftover profile or session-profile agent_token keeps
|
|
489
|
+
// leftover scopes and expiry so billed auth does not remint.
|
|
490
|
+
return leftoverProfile;
|
|
491
|
+
}
|
|
397
492
|
return { token: normalizedEnvToken, provider: null, source: 'env' };
|
|
398
493
|
}
|
|
399
494
|
|
|
@@ -552,7 +647,7 @@ async function performTokenRefresh(credentials, apiRequestJson) {
|
|
|
552
647
|
saved_at: new Date().toISOString(),
|
|
553
648
|
});
|
|
554
649
|
} else {
|
|
555
|
-
saveCredentials(accessToken, newRefreshToken, email, userId, provider);
|
|
650
|
+
saveCredentials(accessToken, newRefreshToken, email, userId, provider, credentials);
|
|
556
651
|
}
|
|
557
652
|
};
|
|
558
653
|
|
|
@@ -586,7 +681,7 @@ async function performTokenRefresh(credentials, apiRequestJson) {
|
|
|
586
681
|
saved_at: new Date().toISOString(),
|
|
587
682
|
});
|
|
588
683
|
} else {
|
|
589
|
-
saveCredentials(accessToken, newRefreshToken, updatedEmail, updatedUserId, updatedProvider);
|
|
684
|
+
saveCredentials(accessToken, newRefreshToken, updatedEmail, updatedUserId, updatedProvider, credentials);
|
|
590
685
|
}
|
|
591
686
|
latestCreds = loadCredentials();
|
|
592
687
|
}
|
|
@@ -603,7 +698,7 @@ async function performTokenRefresh(credentials, apiRequestJson) {
|
|
|
603
698
|
}
|
|
604
699
|
|
|
605
700
|
async function ensureValidCredentials(apiRequestJson, options = {}) {
|
|
606
|
-
let credentials = loadCredentials();
|
|
701
|
+
let credentials = await loadCredentials(apiRequestJson);
|
|
607
702
|
if (!credentials || !credentials.token) {
|
|
608
703
|
return { error: 'not_logged_in' };
|
|
609
704
|
}
|
|
@@ -659,7 +754,8 @@ async function ensureValidCredentials(apiRequestJson, options = {}) {
|
|
|
659
754
|
credentials.refresh_token,
|
|
660
755
|
updatedEmail,
|
|
661
756
|
updatedUserId,
|
|
662
|
-
updatedProvider
|
|
757
|
+
updatedProvider,
|
|
758
|
+
credentials
|
|
663
759
|
);
|
|
664
760
|
}
|
|
665
761
|
}
|