atris 3.65.0 → 3.67.0
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 +27 -0
- package/atris/skills/engines/SKILL.md +2 -2
- package/bin/atris.js +41 -3
- package/commands/agent-client.js +266 -0
- package/commands/auth.js +225 -21
- package/commands/autoland.js +62 -0
- package/commands/brain.js +4 -0
- package/commands/engine.js +23 -1
- package/commands/link.js +367 -0
- package/commands/loops.js +4 -1
- package/commands/member.js +8 -1
- package/commands/orb.js +1 -1
- package/commands/xp.js +2 -0
- package/commands/youtube.js +1 -1
- package/lib/auto-accept-certified.js +89 -9
- package/lib/cleanup-summary.js +129 -0
- package/lib/engine-ask.js +3 -2
- package/lib/engine-registry.js +203 -15
- package/lib/idle-review-sweep.js +130 -0
- package/lib/known-commands.js +2 -2
- package/lib/land-green.js +18 -4
- package/lib/member-result.js +303 -0
- package/lib/roster-models.js +1 -1
- package/lib/runner-command.js +4 -4
- package/lib/task-list-keeper.js +25 -4
- package/lib/trust-tiers.js +131 -14
- package/lib/verify-worktree-rewrite.js +115 -0
- package/lib/voice-gate.js +2 -0
- package/package.json +1 -1
- package/scripts/det/ytnotes +1 -1
- package/utils/agent-client.js +363 -0
- package/utils/api.js +20 -2
- package/utils/auth.js +88 -3
package/utils/auth.js
CHANGED
|
@@ -4,6 +4,7 @@ const { stateHome } = require('../lib/state-home');
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const { exec } = require('child_process');
|
|
6
6
|
const readline = require('readline');
|
|
7
|
+
const agentClient = require('./agent-client');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Open a URL in the system's default browser.
|
|
@@ -395,10 +396,43 @@ function listProfiles() {
|
|
|
395
396
|
}
|
|
396
397
|
}
|
|
397
398
|
|
|
399
|
+
// A saved agent client profile holds only the client id and the secret file
|
|
400
|
+
// path; attach its cached token like the credentials.json path does.
|
|
401
|
+
function activeProfile(profile) {
|
|
402
|
+
return agentClient.isAgentClientCredential(profile) ? agentClient.withCachedToken(profile) : profile;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function readStoredCredentialsFile() {
|
|
406
|
+
try {
|
|
407
|
+
return JSON.parse(fs.readFileSync(getCredentialsPath(), 'utf8'));
|
|
408
|
+
} catch {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Before anything overwrites credentials.json, keep an agent client login as
|
|
414
|
+
// a profile named after its client id, so `atris switch <client_id>` brings
|
|
415
|
+
// it back with its token cache and secret file intact. Returns the name.
|
|
416
|
+
function preserveAgentClientLogin() {
|
|
417
|
+
const stored = readStoredCredentialsFile();
|
|
418
|
+
if (!agentClient.isAgentClientCredential(stored)) return null;
|
|
419
|
+
const name = agentClient.profileNameForClient(stored.client_id);
|
|
420
|
+
saveProfile(name, stored);
|
|
421
|
+
return name;
|
|
422
|
+
}
|
|
423
|
+
|
|
398
424
|
function deleteProfile(name) {
|
|
399
425
|
const profilePath = path.join(getProfilesDir(), `${name}.json`);
|
|
400
426
|
if (fs.existsSync(profilePath)) {
|
|
427
|
+
const profile = loadProfile(name);
|
|
401
428
|
fs.unlinkSync(profilePath);
|
|
429
|
+
if (agentClient.isAgentClientCredential(profile)) {
|
|
430
|
+
// Last reference to this client gone: drop its token cache and secret copy.
|
|
431
|
+
const stored = readStoredCredentialsFile();
|
|
432
|
+
if (!agentClient.isAgentClientCredential(stored) || stored.client_id !== profile.client_id) {
|
|
433
|
+
agentClient.deleteAgentClientFiles(profile.client_id);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
402
436
|
return true;
|
|
403
437
|
}
|
|
404
438
|
return false;
|
|
@@ -415,6 +449,7 @@ function saveCredentials(token, refreshToken, email, userId, provider, extras =
|
|
|
415
449
|
if (decodeJwtClaims(token)?.type === 'agent_access') {
|
|
416
450
|
throw new Error('Refusing to save a scoped agent token as the login token; keep it under agent_token');
|
|
417
451
|
}
|
|
452
|
+
preserveAgentClientLogin();
|
|
418
453
|
const credentialsPath = getCredentialsPath();
|
|
419
454
|
const credentials = {
|
|
420
455
|
...extras,
|
|
@@ -436,6 +471,11 @@ function saveCredentials(token, refreshToken, email, userId, provider, extras =
|
|
|
436
471
|
// Supplying the auth client enables asynchronous repair. Local-only readers stay synchronous.
|
|
437
472
|
function loadCredentials(apiRequestJson) {
|
|
438
473
|
const credentials = readCredentials();
|
|
474
|
+
if (agentClient.isAgentClientCredential(credentials)) {
|
|
475
|
+
// Given the API helper, renew a missing or nearly expired token first.
|
|
476
|
+
if (!apiRequestJson || !agentClient.needsRenewal(credentials)) return credentials;
|
|
477
|
+
return agentClient.ensureAgentClientToken(credentials).catch(() => credentials);
|
|
478
|
+
}
|
|
439
479
|
if (!credentials || credentials.source || decodeJwtClaims(credentials.token)?.type !== 'agent_access') {
|
|
440
480
|
return credentials;
|
|
441
481
|
}
|
|
@@ -502,6 +542,13 @@ function readCredentials() {
|
|
|
502
542
|
return { token: normalizedEnvToken, provider: null, source: 'env' };
|
|
503
543
|
}
|
|
504
544
|
|
|
545
|
+
// 0b. Agent client named by env (ATRIS_CLIENT_ID + secret). ATRIS_TOKEN
|
|
546
|
+
// above still wins, so boxes that inject a raw token are unchanged.
|
|
547
|
+
const envClient = agentClient.envAgentClient();
|
|
548
|
+
if (envClient) {
|
|
549
|
+
return agentClient.withCachedToken(envClient);
|
|
550
|
+
}
|
|
551
|
+
|
|
505
552
|
// 1. Backend-placed key for per-user cloud computers. Missing, malformed,
|
|
506
553
|
// incomplete, and expired files are ignored so later sources keep their
|
|
507
554
|
// existing order.
|
|
@@ -513,7 +560,7 @@ function readCredentials() {
|
|
|
513
560
|
const profileOverride = process.env.ATRIS_PROFILE;
|
|
514
561
|
if (profileOverride) {
|
|
515
562
|
const profile = loadProfile(profileOverride);
|
|
516
|
-
if (profile) return { ...profile, source_profile: profileOverride };
|
|
563
|
+
if (profile) return { ...activeProfile(profile), source_profile: profileOverride };
|
|
517
564
|
const profiles = listProfiles();
|
|
518
565
|
const q = profileOverride.toLowerCase();
|
|
519
566
|
const match = profiles.find(p => p.toLowerCase() === q)
|
|
@@ -521,7 +568,7 @@ function readCredentials() {
|
|
|
521
568
|
|| profiles.find(p => p.toLowerCase().includes(q));
|
|
522
569
|
if (match) {
|
|
523
570
|
const matched = loadProfile(match);
|
|
524
|
-
if (matched) return { ...matched, source_profile: match };
|
|
571
|
+
if (matched) return { ...activeProfile(matched), source_profile: match };
|
|
525
572
|
}
|
|
526
573
|
}
|
|
527
574
|
|
|
@@ -529,7 +576,7 @@ function readCredentials() {
|
|
|
529
576
|
const sessionProfile = getSessionProfile();
|
|
530
577
|
if (sessionProfile) {
|
|
531
578
|
const profile = loadProfile(sessionProfile);
|
|
532
|
-
if (profile) return { ...profile, source_profile: sessionProfile };
|
|
579
|
+
if (profile) return { ...activeProfile(profile), source_profile: sessionProfile };
|
|
533
580
|
}
|
|
534
581
|
|
|
535
582
|
// 4. Global credentials.json
|
|
@@ -542,6 +589,10 @@ function readCredentials() {
|
|
|
542
589
|
try {
|
|
543
590
|
const data = fs.readFileSync(credentialsPath, 'utf8');
|
|
544
591
|
const parsed = JSON.parse(data);
|
|
592
|
+
if (agentClient.isAgentClientCredential(parsed)) {
|
|
593
|
+
// Saved by `atris login --client-id`. The token lives in its own cache.
|
|
594
|
+
return agentClient.withCachedToken(parsed);
|
|
595
|
+
}
|
|
545
596
|
if (!parsed.provider) {
|
|
546
597
|
parsed.provider = null;
|
|
547
598
|
}
|
|
@@ -554,9 +605,32 @@ function readCredentials() {
|
|
|
554
605
|
}
|
|
555
606
|
}
|
|
556
607
|
|
|
608
|
+
// Save an agent client as the active login. The secret itself is never
|
|
609
|
+
// written here, only the file that holds it. Atomic, owner-only.
|
|
610
|
+
function saveAgentClientCredentials(record) {
|
|
611
|
+
const credentialsPath = getCredentialsPath();
|
|
612
|
+
agentClient.writeFileAtomic(credentialsPath, JSON.stringify({
|
|
613
|
+
auth_type: agentClient.AUTH_TYPE,
|
|
614
|
+
client_id: record.client_id,
|
|
615
|
+
client_secret_file: record.client_secret_file,
|
|
616
|
+
scope: record.scope || null,
|
|
617
|
+
provider: agentClient.AUTH_TYPE,
|
|
618
|
+
email: null,
|
|
619
|
+
user_id: null,
|
|
620
|
+
saved_at: new Date().toISOString(),
|
|
621
|
+
}, null, 2));
|
|
622
|
+
}
|
|
623
|
+
|
|
557
624
|
function deleteCredentials() {
|
|
558
625
|
const credentialsPath = getCredentialsPath();
|
|
559
626
|
|
|
627
|
+
const stored = readStoredCredentialsFile();
|
|
628
|
+
if (agentClient.isAgentClientCredential(stored)
|
|
629
|
+
&& !loadProfile(agentClient.profileNameForClient(stored.client_id))) {
|
|
630
|
+
// No saved profile still points at this client, so its files go too.
|
|
631
|
+
agentClient.deleteAgentClientFiles(stored.client_id);
|
|
632
|
+
}
|
|
633
|
+
|
|
560
634
|
if (fs.existsSync(credentialsPath)) {
|
|
561
635
|
fs.unlinkSync(credentialsPath);
|
|
562
636
|
}
|
|
@@ -709,6 +783,14 @@ async function performTokenRefresh(credentials, apiRequestJson) {
|
|
|
709
783
|
|
|
710
784
|
async function ensureValidCredentials(apiRequestJson, options = {}) {
|
|
711
785
|
let credentials = await loadCredentials(apiRequestJson);
|
|
786
|
+
if (agentClient.isAgentClientCredential(credentials)) {
|
|
787
|
+
try {
|
|
788
|
+
const fresh = await agentClient.ensureAgentClientToken(credentials);
|
|
789
|
+
return { credentials: fresh, user: null, source: 'agent_client' };
|
|
790
|
+
} catch (error) {
|
|
791
|
+
return { error: 'token_invalid', detail: agentClient.scrub(error && error.message, []) };
|
|
792
|
+
}
|
|
793
|
+
}
|
|
712
794
|
if (!credentials || !credentials.token) {
|
|
713
795
|
return { error: 'not_logged_in' };
|
|
714
796
|
}
|
|
@@ -860,8 +942,11 @@ module.exports = {
|
|
|
860
942
|
shouldRefreshToken,
|
|
861
943
|
getCredentialsPath,
|
|
862
944
|
saveCredentials,
|
|
945
|
+
saveAgentClientCredentials,
|
|
863
946
|
loadCredentials,
|
|
947
|
+
readActiveCredentials: readCredentials,
|
|
864
948
|
deleteCredentials,
|
|
949
|
+
preserveAgentClientLogin,
|
|
865
950
|
openBrowser,
|
|
866
951
|
promptUser,
|
|
867
952
|
validateAccessToken,
|