@serviceme/devtools-core 0.3.1 → 0.3.3
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/dist/auth.d.mts +2 -592
- package/dist/auth.d.ts +2 -592
- package/dist/auth.js +175 -17
- package/dist/auth.js.map +1 -1
- package/dist/auth.mjs +175 -17
- package/dist/auth.mjs.map +1 -1
- package/dist/device.js +26 -0
- package/dist/device.js.map +1 -1
- package/dist/device.mjs +26 -0
- package/dist/device.mjs.map +1 -1
- package/dist/index-87-_JvQi.d.mts +626 -0
- package/dist/index-87-_JvQi.d.ts +626 -0
- package/dist/index.d.mts +83 -11
- package/dist/index.d.ts +83 -11
- package/dist/index.js +575 -243
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +559 -232
- package/dist/index.mjs.map +1 -1
- package/dist/skill-linker.js.map +1 -1
- package/dist/skill-linker.mjs.map +1 -1
- package/dist/submit.js.map +1 -1
- package/dist/submit.mjs.map +1 -1
- package/dist/toolbox.js +25 -0
- package/dist/toolbox.js.map +1 -1
- package/dist/toolbox.mjs +25 -0
- package/dist/toolbox.mjs.map +1 -1
- package/package.json +2 -2
package/dist/auth.mjs
CHANGED
|
@@ -144,6 +144,18 @@ var AccessControl = class {
|
|
|
144
144
|
}
|
|
145
145
|
};
|
|
146
146
|
|
|
147
|
+
// src/logger.ts
|
|
148
|
+
var noopLogger = {
|
|
149
|
+
debug() {
|
|
150
|
+
},
|
|
151
|
+
info() {
|
|
152
|
+
},
|
|
153
|
+
warn() {
|
|
154
|
+
},
|
|
155
|
+
error() {
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
147
159
|
// src/auth/AuthStateManager.ts
|
|
148
160
|
import { EventEmitter } from "events";
|
|
149
161
|
var AuthStateManager = class {
|
|
@@ -306,6 +318,7 @@ var AuthCore = class {
|
|
|
306
318
|
this.state = opts.stateManager ?? new AuthStateManager();
|
|
307
319
|
this.tokenStore = opts.tokenStore;
|
|
308
320
|
this.accessControl = opts.accessControl;
|
|
321
|
+
this.logger = opts.logger ?? noopLogger;
|
|
309
322
|
}
|
|
310
323
|
/** Snapshot of every account, the active provider, and the last error. */
|
|
311
324
|
status() {
|
|
@@ -327,16 +340,28 @@ var AuthCore = class {
|
|
|
327
340
|
*/
|
|
328
341
|
async login(provider, ui, shouldContinue) {
|
|
329
342
|
const providerImpl = this.registry.get(provider);
|
|
343
|
+
this.logger.info("[AuthCore] Starting device-flow login", { provider });
|
|
330
344
|
try {
|
|
331
345
|
const initial = await providerImpl.requestDeviceFlow();
|
|
332
346
|
await ui(initial);
|
|
347
|
+
if (!initial.deviceCode) {
|
|
348
|
+
this.logger.error("[AuthCore] Provider did not return deviceCode", provider);
|
|
349
|
+
throw new Error("Auth provider did not return deviceCode for device flow completion");
|
|
350
|
+
}
|
|
333
351
|
const session = await providerImpl.completeDeviceFlow(
|
|
334
|
-
initial.
|
|
335
|
-
shouldContinue
|
|
352
|
+
initial.deviceCode,
|
|
353
|
+
shouldContinue,
|
|
354
|
+
initial.pollIntervalMs
|
|
336
355
|
);
|
|
337
|
-
|
|
356
|
+
const result = await this.persistSession(providerImpl, session);
|
|
357
|
+
this.logger.info("[AuthCore] Device-flow login completed", { provider });
|
|
358
|
+
return result;
|
|
338
359
|
} catch (err) {
|
|
339
|
-
|
|
360
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
361
|
+
this.logger.error("[AuthCore] Device-flow login failed", message, {
|
|
362
|
+
provider
|
|
363
|
+
});
|
|
364
|
+
this.state.recordError(message);
|
|
340
365
|
throw err;
|
|
341
366
|
}
|
|
342
367
|
}
|
|
@@ -361,7 +386,10 @@ var AuthCore = class {
|
|
|
361
386
|
if (!provider) return null;
|
|
362
387
|
const account = this.state.getActiveAccount();
|
|
363
388
|
if (!account) return null;
|
|
364
|
-
const envelope = await this.tokenStore.get({
|
|
389
|
+
const envelope = await this.tokenStore.get({
|
|
390
|
+
provider,
|
|
391
|
+
accountId: account.id
|
|
392
|
+
});
|
|
365
393
|
if (!envelope) return null;
|
|
366
394
|
return { provider, account, token: envelope.token };
|
|
367
395
|
}
|
|
@@ -371,9 +399,15 @@ var AuthCore = class {
|
|
|
371
399
|
* every account.
|
|
372
400
|
*/
|
|
373
401
|
async logout(provider) {
|
|
402
|
+
this.logger.info("[AuthCore] Logout requested", {
|
|
403
|
+
provider: provider ?? "all"
|
|
404
|
+
});
|
|
374
405
|
if (!provider) {
|
|
375
406
|
for (const account of this.state.listAccounts()) {
|
|
376
|
-
await this.tokenStore.delete({
|
|
407
|
+
await this.tokenStore.delete({
|
|
408
|
+
provider: account.provider,
|
|
409
|
+
accountId: account.id
|
|
410
|
+
});
|
|
377
411
|
this.state.removeAccount(account.provider, account.id);
|
|
378
412
|
}
|
|
379
413
|
this.state.clearAll();
|
|
@@ -448,6 +482,10 @@ var AuthCore = class {
|
|
|
448
482
|
avatarUrl: meta.avatarUrl,
|
|
449
483
|
expiresAt: meta.expiresAt ?? expiresAt
|
|
450
484
|
};
|
|
485
|
+
this.logger.debug("[AuthCore] Persisting session", {
|
|
486
|
+
provider: meta.provider,
|
|
487
|
+
accountId: meta.id
|
|
488
|
+
});
|
|
451
489
|
await this.tokenStore.set({ provider: meta.provider, accountId: meta.id }, session.token, {
|
|
452
490
|
expiresAt
|
|
453
491
|
});
|
|
@@ -550,11 +588,13 @@ var GitHubAuthProvider = class {
|
|
|
550
588
|
userUrl: config.userUrl ?? DEFAULT_USER_URL,
|
|
551
589
|
scope: config.scope ?? DEFAULT_SCOPE,
|
|
552
590
|
minPollIntervalMs: config.minPollIntervalMs ?? 1e3,
|
|
553
|
-
maxPollIntervalMs: config.maxPollIntervalMs ??
|
|
591
|
+
maxPollIntervalMs: config.maxPollIntervalMs ?? 6e4,
|
|
554
592
|
maxWaitMs: config.maxWaitMs,
|
|
555
593
|
fetchImpl: config.fetchImpl ?? fetch,
|
|
556
594
|
deviceCodeRetryBaseDelayMs: config.deviceCodeRetryBaseDelayMs ?? DEFAULT_DEVICE_CODE_RETRY_BASE_DELAY_MS
|
|
557
595
|
};
|
|
596
|
+
this.logger = config.logger ?? noopLogger;
|
|
597
|
+
this.sleepImpl = config.sleepImpl ?? sleep;
|
|
558
598
|
}
|
|
559
599
|
async requestDeviceFlow(opts) {
|
|
560
600
|
const body = JSON.stringify({
|
|
@@ -563,6 +603,10 @@ var GitHubAuthProvider = class {
|
|
|
563
603
|
});
|
|
564
604
|
let lastNetworkError;
|
|
565
605
|
for (let attempt = 1; attempt <= DEVICE_CODE_MAX_ATTEMPTS; attempt++) {
|
|
606
|
+
this.logger.debug("[GitHubAuthProvider] Requesting device code", {
|
|
607
|
+
attempt,
|
|
608
|
+
maxAttempts: DEVICE_CODE_MAX_ATTEMPTS
|
|
609
|
+
});
|
|
566
610
|
let resp;
|
|
567
611
|
try {
|
|
568
612
|
resp = await this.cfg.fetchImpl(this.cfg.deviceCodeUrl, {
|
|
@@ -576,45 +620,99 @@ var GitHubAuthProvider = class {
|
|
|
576
620
|
});
|
|
577
621
|
} catch (error) {
|
|
578
622
|
if (!isTransientNetworkError(error) || attempt === DEVICE_CODE_MAX_ATTEMPTS) {
|
|
623
|
+
this.logger.error(
|
|
624
|
+
"[GitHubAuthProvider] Device-code request failed (non-retryable)",
|
|
625
|
+
error instanceof Error ? error.message : String(error),
|
|
626
|
+
{ attempt }
|
|
627
|
+
);
|
|
579
628
|
throw error;
|
|
580
629
|
}
|
|
630
|
+
const delayMs = this.cfg.deviceCodeRetryBaseDelayMs * 2 ** (attempt - 1);
|
|
631
|
+
this.logger.warn(
|
|
632
|
+
"[GitHubAuthProvider] Transient network error requesting device code, retrying",
|
|
633
|
+
{
|
|
634
|
+
attempt,
|
|
635
|
+
delayMs,
|
|
636
|
+
error: error instanceof Error ? error.message : String(error)
|
|
637
|
+
}
|
|
638
|
+
);
|
|
581
639
|
lastNetworkError = error;
|
|
582
|
-
await
|
|
640
|
+
await this.sleepImpl(delayMs);
|
|
583
641
|
continue;
|
|
584
642
|
}
|
|
585
643
|
if (!resp.ok) {
|
|
644
|
+
this.logger.error(
|
|
645
|
+
"[GitHubAuthProvider] Device-code request rejected by GitHub",
|
|
646
|
+
`HTTP ${resp.status}`
|
|
647
|
+
);
|
|
586
648
|
throw new Error(`GitHub device-code request failed: HTTP ${resp.status}`);
|
|
587
649
|
}
|
|
588
650
|
const data = await resp.json();
|
|
589
651
|
if (!data.device_code || !data.user_code || !data.verification_uri) {
|
|
652
|
+
this.logger.error(
|
|
653
|
+
"[GitHubAuthProvider] Device-code response missing required fields",
|
|
654
|
+
JSON.stringify(Object.keys(data))
|
|
655
|
+
);
|
|
590
656
|
throw new Error("GitHub device-code response missing required fields");
|
|
591
657
|
}
|
|
658
|
+
this.logger.info("[GitHubAuthProvider] Device code obtained", {
|
|
659
|
+
userCode: data.user_code,
|
|
660
|
+
verificationUri: data.verification_uri,
|
|
661
|
+
expiresInSec: data.expires_in,
|
|
662
|
+
pollIntervalSec: data.interval
|
|
663
|
+
});
|
|
592
664
|
return {
|
|
593
665
|
provider: this.providerId,
|
|
666
|
+
deviceCode: data.device_code,
|
|
594
667
|
userCode: data.user_code,
|
|
595
668
|
verificationUrl: data.verification_uri,
|
|
596
669
|
expiresAt: Date.now() + data.expires_in * 1e3,
|
|
670
|
+
pollIntervalMs: data.interval * 1e3,
|
|
597
671
|
message: `Open ${data.verification_uri} and enter ${data.user_code}`
|
|
598
672
|
};
|
|
599
673
|
}
|
|
600
674
|
throw lastNetworkError ?? new Error("GitHub device-code request failed");
|
|
601
675
|
}
|
|
602
|
-
async completeDeviceFlow(deviceCode, shouldContinue) {
|
|
676
|
+
async completeDeviceFlow(deviceCode, shouldContinue, initialPollIntervalMs) {
|
|
603
677
|
const start = Date.now();
|
|
604
|
-
let pollIntervalMs = this.cfg.minPollIntervalMs;
|
|
678
|
+
let pollIntervalMs = Math.max(this.cfg.minPollIntervalMs, initialPollIntervalMs ?? 0);
|
|
605
679
|
let consecutiveSlowDown = 0;
|
|
680
|
+
let pollCount = 0;
|
|
681
|
+
this.logger.info("[GitHubAuthProvider] Starting device-flow polling", {
|
|
682
|
+
initialPollIntervalMs: pollIntervalMs,
|
|
683
|
+
maxWaitMs: this.cfg.maxWaitMs
|
|
684
|
+
});
|
|
606
685
|
while (true) {
|
|
607
686
|
if (shouldContinue && !shouldContinue()) {
|
|
687
|
+
this.logger.info("[GitHubAuthProvider] Device flow cancelled by caller", {
|
|
688
|
+
elapsedMs: Date.now() - start,
|
|
689
|
+
pollCount
|
|
690
|
+
});
|
|
608
691
|
throw new Error("GitHub device flow cancelled by caller");
|
|
609
692
|
}
|
|
610
693
|
const elapsed = Date.now() - start;
|
|
611
694
|
if (this.cfg.maxWaitMs !== void 0 && elapsed > this.cfg.maxWaitMs) {
|
|
695
|
+
this.logger.warn("[GitHubAuthProvider] Device flow exceeded max wait time", {
|
|
696
|
+
elapsedMs: elapsed,
|
|
697
|
+
maxWaitMs: this.cfg.maxWaitMs,
|
|
698
|
+
pollCount
|
|
699
|
+
});
|
|
612
700
|
throw new Error("GitHub device flow exceeded max wait time");
|
|
613
701
|
}
|
|
614
|
-
await
|
|
702
|
+
await this.sleepImpl(pollIntervalMs);
|
|
615
703
|
if (shouldContinue && !shouldContinue()) {
|
|
704
|
+
this.logger.info("[GitHubAuthProvider] Device flow cancelled by caller", {
|
|
705
|
+
elapsedMs: Date.now() - start,
|
|
706
|
+
pollCount
|
|
707
|
+
});
|
|
616
708
|
throw new Error("GitHub device flow cancelled by caller");
|
|
617
709
|
}
|
|
710
|
+
pollCount++;
|
|
711
|
+
this.logger.debug("[GitHubAuthProvider] Polling for authorization", {
|
|
712
|
+
pollCount,
|
|
713
|
+
elapsedMs: Date.now() - start,
|
|
714
|
+
pollIntervalMs
|
|
715
|
+
});
|
|
618
716
|
let resp;
|
|
619
717
|
try {
|
|
620
718
|
resp = await this.cfg.fetchImpl(this.cfg.tokenUrl, {
|
|
@@ -632,19 +730,59 @@ var GitHubAuthProvider = class {
|
|
|
632
730
|
});
|
|
633
731
|
} catch (error) {
|
|
634
732
|
if (!isTransientNetworkError(error)) {
|
|
733
|
+
this.logger.error(
|
|
734
|
+
"[GitHubAuthProvider] Non-retryable error while polling token endpoint",
|
|
735
|
+
error instanceof Error ? error.message : String(error),
|
|
736
|
+
{ pollCount }
|
|
737
|
+
);
|
|
635
738
|
throw error;
|
|
636
739
|
}
|
|
637
|
-
|
|
740
|
+
const nextPollIntervalMs = Math.min(
|
|
741
|
+
Math.round(pollIntervalMs * 1.5),
|
|
742
|
+
this.cfg.maxPollIntervalMs
|
|
743
|
+
);
|
|
744
|
+
this.logger.warn(
|
|
745
|
+
"[GitHubAuthProvider] Transient network error while polling, backing off",
|
|
746
|
+
{
|
|
747
|
+
pollCount,
|
|
748
|
+
error: error instanceof Error ? error.message : String(error),
|
|
749
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
750
|
+
nextPollIntervalMs
|
|
751
|
+
}
|
|
752
|
+
);
|
|
753
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
638
754
|
continue;
|
|
639
755
|
}
|
|
640
756
|
if (!resp.ok) {
|
|
641
|
-
|
|
757
|
+
const nextPollIntervalMs = Math.min(
|
|
758
|
+
Math.round(pollIntervalMs * 1.5),
|
|
759
|
+
this.cfg.maxPollIntervalMs
|
|
760
|
+
);
|
|
761
|
+
this.logger.warn(
|
|
762
|
+
"[GitHubAuthProvider] Token endpoint returned non-OK status, backing off",
|
|
763
|
+
{
|
|
764
|
+
pollCount,
|
|
765
|
+
status: resp.status,
|
|
766
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
767
|
+
nextPollIntervalMs
|
|
768
|
+
}
|
|
769
|
+
);
|
|
770
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
642
771
|
continue;
|
|
643
772
|
}
|
|
644
773
|
const data = await resp.json();
|
|
645
774
|
if (data.access_token) {
|
|
775
|
+
this.logger.info("[GitHubAuthProvider] Authorization granted, fetching user profile", {
|
|
776
|
+
pollCount,
|
|
777
|
+
elapsedMs: Date.now() - start
|
|
778
|
+
});
|
|
646
779
|
const rawUser = await this.fetchGitHubUser(data.access_token);
|
|
647
780
|
const email = await this.resolveEmail(data.access_token, rawUser);
|
|
781
|
+
this.logger.info("[GitHubAuthProvider] Device-flow login completed", {
|
|
782
|
+
pollCount,
|
|
783
|
+
elapsedMs: Date.now() - start,
|
|
784
|
+
login: rawUser.login
|
|
785
|
+
});
|
|
648
786
|
return {
|
|
649
787
|
token: data.access_token,
|
|
650
788
|
refreshToken: data.refresh_token,
|
|
@@ -659,22 +797,42 @@ var GitHubAuthProvider = class {
|
|
|
659
797
|
};
|
|
660
798
|
}
|
|
661
799
|
if (data.error === "authorization_pending") {
|
|
800
|
+
this.logger.debug("[GitHubAuthProvider] Authorization still pending", {
|
|
801
|
+
pollCount,
|
|
802
|
+
elapsedMs: Date.now() - start
|
|
803
|
+
});
|
|
662
804
|
continue;
|
|
663
805
|
}
|
|
664
806
|
if (data.error === "slow_down") {
|
|
665
807
|
consecutiveSlowDown++;
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
808
|
+
const nextPollIntervalMs = Math.min(pollIntervalMs + 5e3, this.cfg.maxPollIntervalMs);
|
|
809
|
+
this.logger.warn("[GitHubAuthProvider] GitHub requested slower polling (slow_down)", {
|
|
810
|
+
pollCount,
|
|
811
|
+
consecutiveSlowDown,
|
|
812
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
813
|
+
nextPollIntervalMs
|
|
814
|
+
});
|
|
815
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
670
816
|
continue;
|
|
671
817
|
}
|
|
672
818
|
if (data.error === "access_denied") {
|
|
819
|
+
this.logger.warn("[GitHubAuthProvider] User denied authorization", {
|
|
820
|
+
pollCount
|
|
821
|
+
});
|
|
673
822
|
throw new Error("User denied authorization");
|
|
674
823
|
}
|
|
675
824
|
if (data.error === "expired_token") {
|
|
825
|
+
this.logger.warn(
|
|
826
|
+
"[GitHubAuthProvider] Device code expired before authorization completed",
|
|
827
|
+
{ pollCount, elapsedMs: Date.now() - start }
|
|
828
|
+
);
|
|
676
829
|
throw new Error("GitHub device code expired \u2014 restart the flow");
|
|
677
830
|
}
|
|
831
|
+
this.logger.error(
|
|
832
|
+
"[GitHubAuthProvider] Unexpected device-flow error from token endpoint",
|
|
833
|
+
data.error ?? "unknown",
|
|
834
|
+
{ pollCount }
|
|
835
|
+
);
|
|
678
836
|
throw new Error(`GitHub device flow error: ${data.error ?? "unknown"}`);
|
|
679
837
|
}
|
|
680
838
|
}
|