atris 3.43.0 → 3.45.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/atris/skills/design/SKILL.md +7 -1
- package/atris/skills/engines/SKILL.md +44 -13
- package/atris/team/customer-lead/MEMBER.md +45 -0
- package/atris/team/customer-lead/SOUL.md +33 -0
- package/atris/team/customer-lead/START_HERE.md +7 -0
- package/atris/team/customer-lead/skills/customer-commitments/SKILL.md +45 -0
- package/atris/team/improver/MEMBER.md +33 -0
- package/bin/atris.js +36 -3
- package/commands/aeo.js +5 -2
- package/commands/align.js +5 -2
- package/commands/autoland.js +15 -1
- package/commands/caretaker.js +303 -0
- package/commands/clean.js +76 -0
- package/commands/computer.js +5 -2
- package/commands/engine-watch.js +212 -0
- package/commands/engine.js +99 -11
- package/commands/founder.js +304 -0
- package/commands/human-missions.js +844 -0
- package/commands/improve.js +29 -6
- package/commands/init.js +16 -7
- package/commands/mission.js +124 -69
- package/commands/pull.js +5 -2
- package/commands/push.js +5 -2
- package/commands/slop.js +34 -3
- package/commands/task.js +51 -4
- package/commands/team.js +329 -13
- package/commands/terminal.js +5 -2
- package/commands/verify.js +99 -6
- package/commands/workflow.js +10 -3
- package/commands/worktree.js +119 -4
- package/lib/auto-accept-certified.js +302 -0
- package/lib/cloud-mission.js +59 -2
- package/lib/conductor-artifacts.js +1 -1
- package/lib/dispatch-scout.js +386 -0
- package/lib/engine-ask.js +645 -0
- package/lib/engine-job-lifecycle.js +65 -0
- package/lib/engine-receipt-sweep.js +98 -0
- package/lib/engine-registry.js +2 -2
- package/lib/engine-validate.js +382 -0
- package/lib/fleet.js +459 -106
- package/lib/known-commands.js +2 -2
- package/lib/member-alive.js +2 -2
- package/lib/policy-lessons.js +70 -0
- package/lib/receipt-evidence.js +56 -1
- package/lib/runner-command.js +1 -1
- package/lib/secret-gateway.js +588 -0
- package/lib/team-presence.js +13 -1
- package/lib/voice-gate.js +6 -0
- package/lib/wish-audit.js +5 -205
- package/lib/wish-delegate.js +5 -2
- package/package.json +6 -1
- package/utils/auth.js +56 -9
package/lib/wish-audit.js
CHANGED
|
@@ -189,84 +189,6 @@ const VAGUE_SUPERLATIVE_WORDS = new Set([
|
|
|
189
189
|
'perfect',
|
|
190
190
|
'smartest',
|
|
191
191
|
]);
|
|
192
|
-
const GENERIC_INTERPRETATION_VERBS = new Set([
|
|
193
|
-
'add',
|
|
194
|
-
'build',
|
|
195
|
-
'change',
|
|
196
|
-
'create',
|
|
197
|
-
'do',
|
|
198
|
-
'fix',
|
|
199
|
-
'get',
|
|
200
|
-
'improve',
|
|
201
|
-
'make',
|
|
202
|
-
'polish',
|
|
203
|
-
'tweak',
|
|
204
|
-
'update',
|
|
205
|
-
]);
|
|
206
|
-
const AMBIGUITY_INTERPRETATIONS = {
|
|
207
|
-
'vague-superlative': {
|
|
208
|
-
candidates: ['faster to use', 'smarter by default', 'more complete'],
|
|
209
|
-
recommendedIndex: 1,
|
|
210
|
-
ask: 'which should I optimize for',
|
|
211
|
-
},
|
|
212
|
-
'missing-audience': {
|
|
213
|
-
candidates: ['solo operator', 'team member', 'end user'],
|
|
214
|
-
recommendedIndex: 0,
|
|
215
|
-
ask: 'who is this for',
|
|
216
|
-
},
|
|
217
|
-
'missing-first-slice': {
|
|
218
|
-
candidates: ['capture path', 'resurfacing rule', 'closure loop'],
|
|
219
|
-
recommendedIndex: 1,
|
|
220
|
-
ask: 'what should I change first',
|
|
221
|
-
},
|
|
222
|
-
};
|
|
223
|
-
const SUBJECT_KIND_INTERPRETATIONS = {
|
|
224
|
-
list: {
|
|
225
|
-
'vague-superlative': {
|
|
226
|
-
candidates: ['fastest capture', 'smartest resurfacing', 'most closure'],
|
|
227
|
-
recommendedIndex: 1,
|
|
228
|
-
},
|
|
229
|
-
'missing-first-slice': {
|
|
230
|
-
candidates: ['capture path', 'resurfacing rule', 'closure loop'],
|
|
231
|
-
recommendedIndex: 1,
|
|
232
|
-
},
|
|
233
|
-
},
|
|
234
|
-
page: {
|
|
235
|
-
'vague-superlative': {
|
|
236
|
-
candidates: ['clearer layout', 'faster scanning', 'stronger action'],
|
|
237
|
-
recommendedIndex: 0,
|
|
238
|
-
},
|
|
239
|
-
'missing-first-slice': {
|
|
240
|
-
candidates: ['top section', 'content hierarchy', 'primary action'],
|
|
241
|
-
recommendedIndex: 1,
|
|
242
|
-
},
|
|
243
|
-
},
|
|
244
|
-
loop: {
|
|
245
|
-
'vague-superlative': {
|
|
246
|
-
candidates: ['fewer steps', 'smarter defaults', 'more reliable completion'],
|
|
247
|
-
recommendedIndex: 1,
|
|
248
|
-
},
|
|
249
|
-
'missing-first-slice': {
|
|
250
|
-
candidates: ['entry path', 'decision rule', 'completion check'],
|
|
251
|
-
recommendedIndex: 1,
|
|
252
|
-
},
|
|
253
|
-
},
|
|
254
|
-
generic: {
|
|
255
|
-
'vague-superlative': {
|
|
256
|
-
candidates: ['faster to use', 'smarter by default', 'more complete'],
|
|
257
|
-
recommendedIndex: 1,
|
|
258
|
-
},
|
|
259
|
-
'missing-first-slice': {
|
|
260
|
-
candidates: ['first step', 'default behavior', 'completion path'],
|
|
261
|
-
recommendedIndex: 1,
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
};
|
|
265
|
-
const SUBJECT_KIND_WORDS = {
|
|
266
|
-
list: new Set(['backlog', 'board', 'inbox', 'kanban', 'list', 'queue', 'reminder', 'task', 'todo']),
|
|
267
|
-
page: new Set(['card', 'dashboard', 'form', 'homepage', 'landing', 'modal', 'page', 'panel', 'screen', 'site', 'surface', 'ui', 'view', 'website']),
|
|
268
|
-
loop: new Set(['auth', 'automation', 'cadence', 'checkout', 'flow', 'import', 'login', 'loop', 'onboarding', 'pipeline', 'process', 'routine', 'signup', 'sync', 'workflow']),
|
|
269
|
-
};
|
|
270
192
|
// A conjunction followed by a determiner ("an ml researcher and a 2nd grade
|
|
271
193
|
// teacher") joins noun phrases inside one clause, not two separate wishes.
|
|
272
194
|
const PART_JOINER_WORDS = new Set(['and', 'plus', 'also', 'adn', 'nad', 'annd']);
|
|
@@ -770,12 +692,6 @@ function sharesMeaningfulWords(left, right) {
|
|
|
770
692
|
return false;
|
|
771
693
|
}
|
|
772
694
|
|
|
773
|
-
function capitalizeFirst(text) {
|
|
774
|
-
const clean = String(text || '').trim();
|
|
775
|
-
if (!clean) return '';
|
|
776
|
-
return clean.charAt(0).toUpperCase() + clean.slice(1);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
695
|
function singularInterpretationWord(word) {
|
|
780
696
|
const clean = cleanWord(word);
|
|
781
697
|
if (clean.length > 3 && clean.endsWith('s')) return clean.slice(0, -1);
|
|
@@ -799,15 +715,6 @@ function interpretationWords(text, subject) {
|
|
|
799
715
|
return values;
|
|
800
716
|
}
|
|
801
717
|
|
|
802
|
-
function interpretationVerb(text) {
|
|
803
|
-
for (const word of wordList(text)) {
|
|
804
|
-
const clean = cleanWord(word);
|
|
805
|
-
if (!clean || GENERIC_INTERPRETATION_VERBS.has(clean)) continue;
|
|
806
|
-
if (VERBS.has(clean)) return clean;
|
|
807
|
-
}
|
|
808
|
-
return '';
|
|
809
|
-
}
|
|
810
|
-
|
|
811
718
|
function superlativeWord(text) {
|
|
812
719
|
for (const word of wordList(text)) {
|
|
813
720
|
const clean = cleanWord(word);
|
|
@@ -818,114 +725,12 @@ function superlativeWord(text) {
|
|
|
818
725
|
|
|
819
726
|
function interpretationContext(text, subject) {
|
|
820
727
|
const nouns = interpretationWords(text, subject);
|
|
821
|
-
const topic = nouns.slice(0, 2).join(' ');
|
|
822
728
|
return {
|
|
823
|
-
|
|
824
|
-
topicWords: nouns,
|
|
825
|
-
topic,
|
|
826
|
-
topicHead: nouns[0] || '',
|
|
827
|
-
action: interpretationVerb(text),
|
|
729
|
+
topic: nouns.slice(0, 2).join(' '),
|
|
828
730
|
superlative: superlativeWord(text),
|
|
829
731
|
};
|
|
830
732
|
}
|
|
831
733
|
|
|
832
|
-
function subjectKindForInterpretation(ctx) {
|
|
833
|
-
const topicWords = Array.isArray(ctx.topicWords) ? ctx.topicWords : [];
|
|
834
|
-
if (!topicWords.length) return 'generic';
|
|
835
|
-
const words = new Set(topicWords);
|
|
836
|
-
for (const [kind, kindWords] of Object.entries(SUBJECT_KIND_WORDS)) {
|
|
837
|
-
if (Array.from(words).some((word) => kindWords.has(word))) return kind;
|
|
838
|
-
}
|
|
839
|
-
return 'generic';
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
function subjectKindInterpretation(kind, ctx) {
|
|
843
|
-
const subjectKind = subjectKindForInterpretation(ctx);
|
|
844
|
-
return (SUBJECT_KIND_INTERPRETATIONS[subjectKind] && SUBJECT_KIND_INTERPRETATIONS[subjectKind][kind])
|
|
845
|
-
|| (SUBJECT_KIND_INTERPRETATIONS.generic && SUBJECT_KIND_INTERPRETATIONS.generic[kind])
|
|
846
|
-
|| null;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
function uniqueInterpretations(values) {
|
|
850
|
-
const seen = new Set();
|
|
851
|
-
const unique = [];
|
|
852
|
-
for (const value of values) {
|
|
853
|
-
const clean = String(value || '').trim().replace(/\s+/g, ' ');
|
|
854
|
-
const key = clean.toLowerCase();
|
|
855
|
-
if (!clean || seen.has(key)) continue;
|
|
856
|
-
seen.add(key);
|
|
857
|
-
unique.push(clean);
|
|
858
|
-
}
|
|
859
|
-
return unique;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
function joinInterpretations(values) {
|
|
863
|
-
if (values.length <= 1) return values[0] || '';
|
|
864
|
-
if (values.length === 2) return `${values[0]} or ${values[1]}`;
|
|
865
|
-
return `${values.slice(0, -1).join(', ')}, or ${values[values.length - 1]}`;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
function interpretationCandidates(kind, ctx) {
|
|
869
|
-
const domain = kind === 'vague-superlative' || kind === 'missing-first-slice'
|
|
870
|
-
? subjectKindInterpretation(kind, ctx)
|
|
871
|
-
: null;
|
|
872
|
-
const table = domain || AMBIGUITY_INTERPRETATIONS[kind];
|
|
873
|
-
const candidates = table ? [...table.candidates] : [];
|
|
874
|
-
if (kind === 'missing-audience' && ctx.topic) candidates[0] = `${ctx.topic} owner`;
|
|
875
|
-
if (kind === 'missing-first-slice' && domain && ctx.topic && subjectKindForInterpretation(ctx) !== 'generic') {
|
|
876
|
-
candidates[0] = `${ctx.topic} ${candidates[0]}`;
|
|
877
|
-
}
|
|
878
|
-
if (kind === 'missing-first-slice' && !domain && ctx.topic) candidates[0] = `${ctx.topic} capture path`;
|
|
879
|
-
if (kind === 'missing-first-slice' && !domain && ctx.action) candidates[0] = `${ctx.action} path`;
|
|
880
|
-
return uniqueInterpretations(candidates).slice(0, 3);
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
function recommendedInterpretationIndex(kind, ctx, candidates) {
|
|
884
|
-
const words = new Set(ctx.words);
|
|
885
|
-
if (kind === 'missing-audience') {
|
|
886
|
-
if (words.has('team') || words.has('member')) return Math.min(1, candidates.length - 1);
|
|
887
|
-
if (words.has('customer') || words.has('visitor') || words.has('client')) return Math.min(2, candidates.length - 1);
|
|
888
|
-
}
|
|
889
|
-
if (kind === 'vague-superlative' || kind === 'missing-first-slice') {
|
|
890
|
-
if (['fast', 'faster', 'fastest', 'speed', 'quick', 'quickly'].some((word) => words.has(word))) return 0;
|
|
891
|
-
if (['done', 'finish', 'finished', 'close', 'closure', 'complete', 'ship'].some((word) => words.has(word))) {
|
|
892
|
-
return Math.min(2, candidates.length - 1);
|
|
893
|
-
}
|
|
894
|
-
if (['todo', 'task', 'tasks', 'list', 'backlog', 'inbox', 'remind', 'reminder', 'surface', 'resurface'].some((word) => words.has(word))) {
|
|
895
|
-
return Math.min(1, candidates.length - 1);
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
const domain = kind === 'vague-superlative' || kind === 'missing-first-slice'
|
|
899
|
-
? subjectKindInterpretation(kind, ctx)
|
|
900
|
-
: null;
|
|
901
|
-
const table = domain || AMBIGUITY_INTERPRETATIONS[kind];
|
|
902
|
-
return Math.min(table ? table.recommendedIndex : 0, candidates.length - 1);
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
function recommendedInterpretationName(candidate) {
|
|
906
|
-
return String(candidate || '').replace(/^(?:fastest|smartest|most)\s+/i, '').trim();
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
function interpretationLabel(kind, ctx) {
|
|
910
|
-
if (kind === 'vague-superlative') {
|
|
911
|
-
if (ctx.superlative && ctx.topic) return `${capitalizeFirst(ctx.superlative)} ${ctx.topic}`;
|
|
912
|
-
return capitalizeFirst(ctx.superlative || ctx.topic || 'this');
|
|
913
|
-
}
|
|
914
|
-
const topic = ctx.topic ? `${ctx.topic} ` : '';
|
|
915
|
-
if (kind === 'missing-audience') return capitalizeFirst(`${topic}audience`);
|
|
916
|
-
if (kind === 'missing-first-slice') return capitalizeFirst(`${topic}first slice`);
|
|
917
|
-
return capitalizeFirst(ctx.topic || 'this');
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
function buildInterpretationQuestion(text, subject, kind) {
|
|
921
|
-
const table = AMBIGUITY_INTERPRETATIONS[kind];
|
|
922
|
-
const ctx = interpretationContext(text, subject);
|
|
923
|
-
const candidates = interpretationCandidates(kind, ctx);
|
|
924
|
-
const recommended = recommendedInterpretationName(candidates[recommendedInterpretationIndex(kind, ctx, candidates)]);
|
|
925
|
-
const label = interpretationLabel(kind, ctx);
|
|
926
|
-
return `${label} could mean ${joinInterpretations(candidates)}. I would bet on ${recommended}, so ${table.ask}?`;
|
|
927
|
-
}
|
|
928
|
-
|
|
929
734
|
function hasSubjectBearingSuperlative(text, subject) {
|
|
930
735
|
const ctx = interpretationContext(text, subject);
|
|
931
736
|
return Boolean(ctx.superlative && ctx.topic);
|
|
@@ -936,15 +741,10 @@ function buildClarityQuestions(text, subject, vague, options = {}) {
|
|
|
936
741
|
? hasSubjectBearingSuperlative(text, subject)
|
|
937
742
|
: !!options.subjectSuperlative;
|
|
938
743
|
if (!vague && !subjectSuperlative) return [];
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
if (!AUDIENCE_WORDS.test(text)) {
|
|
944
|
-
questions.push(buildInterpretationQuestion(text, subject, 'missing-audience'));
|
|
945
|
-
}
|
|
946
|
-
questions.push(buildInterpretationQuestion(text, subject, 'missing-first-slice'));
|
|
947
|
-
return questions;
|
|
744
|
+
const topic = interpretationContext(text, subject).topic;
|
|
745
|
+
return [topic
|
|
746
|
+
? `What should be different about ${topic} when this is done?`
|
|
747
|
+
: 'What should be different when this is done?'];
|
|
948
748
|
}
|
|
949
749
|
|
|
950
750
|
function priorQuestionRowForAuditTextAnyQuestion(text, root) {
|
package/lib/wish-delegate.js
CHANGED
|
@@ -212,7 +212,7 @@ function wishTitle(wish) {
|
|
|
212
212
|
|
|
213
213
|
// One question at a time, always ending with how to answer it.
|
|
214
214
|
function printQuestion(wish, question) {
|
|
215
|
-
console.log(`Got it
|
|
215
|
+
console.log(`Got it: ${quoteText(wish.text || '')}.`);
|
|
216
216
|
console.log(String(question || 'What should be different when this wish comes true?'));
|
|
217
217
|
console.log('Answer with: atris wish answer "your words"');
|
|
218
218
|
}
|
|
@@ -1097,7 +1097,7 @@ function builderWish(wish, audit, root, asJson) {
|
|
|
1097
1097
|
|
|
1098
1098
|
// Plain per-part reply: what started, what lives elsewhere, and one question per unclear part.
|
|
1099
1099
|
function printDecomposed(wish, parts, delegatedParts, waitingParts) {
|
|
1100
|
-
console.log(`Got it
|
|
1100
|
+
console.log(`Got it: ${quoteText(wish.text || '')}. It has ${parts.length} parts.`);
|
|
1101
1101
|
delegatedParts.forEach((part) => {
|
|
1102
1102
|
if (part.attached_to_existing_flight) {
|
|
1103
1103
|
printGranted(part.text, null, {
|
|
@@ -1267,6 +1267,9 @@ function intakeQuestionSubject(question) {
|
|
|
1267
1267
|
const interpretationMarker = ' could mean ';
|
|
1268
1268
|
const interpretationIndex = normalized.indexOf(interpretationMarker);
|
|
1269
1269
|
if (interpretationIndex > 0) return `interpretation:${normalized.slice(0, interpretationIndex)}`;
|
|
1270
|
+
const grounded = normalized.match(/^what should be different about (.+?) when this is done\?$/);
|
|
1271
|
+
if (grounded) return `interpretation:${grounded[1]}`;
|
|
1272
|
+
if (normalized === 'what should be different when this is done?') return 'interpretation:this';
|
|
1270
1273
|
return `question:${normalized}`;
|
|
1271
1274
|
}
|
|
1272
1275
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atris",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.45.1",
|
|
4
4
|
"description": "you say what you want in plain words. atris builds it, checks it, and shows you proof.",
|
|
5
5
|
"main": "bin/atris.js",
|
|
6
6
|
"bin": {
|
|
@@ -43,6 +43,11 @@
|
|
|
43
43
|
"atris/team/mission-lead/MEMBER.md",
|
|
44
44
|
"atris/team/mission-lead/MISSION.md",
|
|
45
45
|
"atris/team/mission-lead/START_HERE.md",
|
|
46
|
+
"atris/team/improver/MEMBER.md",
|
|
47
|
+
"atris/team/customer-lead/MEMBER.md",
|
|
48
|
+
"atris/team/customer-lead/SOUL.md",
|
|
49
|
+
"atris/team/customer-lead/START_HERE.md",
|
|
50
|
+
"atris/team/customer-lead/skills/customer-commitments/SKILL.md",
|
|
46
51
|
"atris/team/opus-overnight/MEMBER.md",
|
|
47
52
|
"atris/team/opus-overnight/MISSION.md",
|
|
48
53
|
"atris/team/opus-overnight/START_HERE.md",
|
package/utils/auth.js
CHANGED
|
@@ -407,13 +407,27 @@ async function validateAccessToken(token, apiRequestJson) {
|
|
|
407
407
|
});
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
function refreshProviderHint(provider, refreshToken) {
|
|
411
|
+
const hint = typeof provider === 'string' ? provider.trim().toLowerCase() : '';
|
|
412
|
+
if (!hint) return null;
|
|
413
|
+
// provider=google makes /auth/refresh skip app-JWT refresh and POST the
|
|
414
|
+
// minted refresh JWT to Google OAuth, which returns google_refresh_failed.
|
|
415
|
+
// Only send that hint when the stored token is actually a Google OAuth
|
|
416
|
+
// refresh token (they start with 1//). Pack refresh already strips this.
|
|
417
|
+
if (hint === 'google' && !String(refreshToken || '').startsWith('1//')) {
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
return hint;
|
|
421
|
+
}
|
|
422
|
+
|
|
410
423
|
async function refreshAccessToken(refreshToken, provider, apiRequestJson) {
|
|
411
424
|
if (!refreshToken) {
|
|
412
425
|
return { ok: false, status: 0, error: 'Missing refresh token' };
|
|
413
426
|
}
|
|
414
427
|
const body = { refresh_token: refreshToken };
|
|
415
|
-
|
|
416
|
-
|
|
428
|
+
const hint = refreshProviderHint(provider, refreshToken);
|
|
429
|
+
if (hint) {
|
|
430
|
+
body.provider = hint;
|
|
417
431
|
}
|
|
418
432
|
return apiRequestJson('/auth/refresh', {
|
|
419
433
|
method: 'POST',
|
|
@@ -421,6 +435,24 @@ async function refreshAccessToken(refreshToken, provider, apiRequestJson) {
|
|
|
421
435
|
});
|
|
422
436
|
}
|
|
423
437
|
|
|
438
|
+
function isAuthFailure(result) {
|
|
439
|
+
const status = result && result.status;
|
|
440
|
+
return status === 401 || status === 403;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function printAuthRequired() {
|
|
444
|
+
console.error('Auth problem. Run: atris login');
|
|
445
|
+
console.error('Check with: atris whoami');
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function abortOnAuthFailure(result, printedWake = false, exitFn = (code) => process.exit(code)) {
|
|
449
|
+
if (!isAuthFailure(result)) return false;
|
|
450
|
+
if (printedWake) console.log('auth failed');
|
|
451
|
+
printAuthRequired();
|
|
452
|
+
exitFn(1);
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
|
|
424
456
|
async function performTokenRefresh(credentials, apiRequestJson) {
|
|
425
457
|
if (!credentials || !credentials.refresh_token) {
|
|
426
458
|
return { ok: false, error: 'missing_refresh_token' };
|
|
@@ -535,13 +567,24 @@ async function ensureValidCredentials(apiRequestJson, options = {}) {
|
|
|
535
567
|
updatedProvider !== credentials.provider ||
|
|
536
568
|
updatedUserId !== credentials.user_id)
|
|
537
569
|
) {
|
|
538
|
-
|
|
539
|
-
credentials
|
|
540
|
-
credentials.
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
570
|
+
if (credentials.source_profile) {
|
|
571
|
+
const { source_profile, source, ...rest } = { ...credentials };
|
|
572
|
+
saveProfile(credentials.source_profile, {
|
|
573
|
+
...rest,
|
|
574
|
+
email: updatedEmail,
|
|
575
|
+
user_id: updatedUserId,
|
|
576
|
+
provider: updatedProvider,
|
|
577
|
+
saved_at: new Date().toISOString(),
|
|
578
|
+
});
|
|
579
|
+
} else {
|
|
580
|
+
saveCredentials(
|
|
581
|
+
credentials.token,
|
|
582
|
+
credentials.refresh_token,
|
|
583
|
+
updatedEmail,
|
|
584
|
+
updatedUserId,
|
|
585
|
+
updatedProvider
|
|
586
|
+
);
|
|
587
|
+
}
|
|
545
588
|
}
|
|
546
589
|
|
|
547
590
|
return {
|
|
@@ -639,8 +682,12 @@ module.exports = {
|
|
|
639
682
|
promptUser,
|
|
640
683
|
validateAccessToken,
|
|
641
684
|
refreshAccessToken,
|
|
685
|
+
refreshProviderHint,
|
|
642
686
|
performTokenRefresh,
|
|
643
687
|
ensureValidCredentials,
|
|
688
|
+
isAuthFailure,
|
|
689
|
+
printAuthRequired,
|
|
690
|
+
abortOnAuthFailure,
|
|
644
691
|
fetchMyAgents,
|
|
645
692
|
displayAccountSummary,
|
|
646
693
|
// Profile switching
|