@aexol/opencode-wizard 0.1.9 → 0.1.12

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/server.d.ts CHANGED
@@ -226,6 +226,11 @@ export declare const resolvePluginStatusSnapshot: ({ worktree, directory, signal
226
226
  directory: string;
227
227
  signal: AbortSignal;
228
228
  }) => Promise<PluginStatusSnapshot>;
229
+ export declare const resolvePluginStatusSnapshotWithAuthBootstrap: ({ worktree, directory, signal, }: {
230
+ worktree: string;
231
+ directory: string;
232
+ signal: AbortSignal;
233
+ }) => Promise<PluginStatusSnapshot>;
229
234
  declare const _default: {
230
235
  id: string;
231
236
  server: OpencodePluginServer;
package/dist/server.js CHANGED
@@ -43,6 +43,13 @@ const createIdleLoginBootstrapSnapshot = () => ({
43
43
  email: null,
44
44
  message: null
45
45
  });
46
+ const STATUS_PATH_LOGIN_RETRY_COOLDOWN_MS = 60_000;
47
+ const statusPathLoginBootstrap = {
48
+ promise: null,
49
+ status: 'idle',
50
+ message: null,
51
+ failedAt: null
52
+ };
46
53
  const importOpencodePluginModule = new Function('specifier', 'return import(specifier)');
47
54
  export const AVAILABLE_PUBLISHED_SKILL_TOOLS = ['opencode_wizard_published_skills_fetch', 'opencode_wizard_status'];
48
55
  export const NATIVE_SKILLS_URL_COMPATIBILITY = {
@@ -672,6 +679,76 @@ export const resolvePluginStatusSnapshot = async ({
672
679
  catalog: fetchResult.ok ? toPublishedSkillCatalog(fetchResult.payload) : null
673
680
  };
674
681
  };
682
+ const withStatusMessage = (snapshot, message) => ({
683
+ ...snapshot,
684
+ message
685
+ });
686
+ const startStatusPathLoginBootstrap = (worktree, config) => {
687
+ if (statusPathLoginBootstrap.promise) return;
688
+ if (statusPathLoginBootstrap.status === 'failed' && statusPathLoginBootstrap.failedAt && Date.now() - statusPathLoginBootstrap.failedAt < STATUS_PATH_LOGIN_RETRY_COOLDOWN_MS) {
689
+ return;
690
+ }
691
+ statusPathLoginBootstrap.status = 'pending';
692
+ statusPathLoginBootstrap.message = 'Browser login started automatically from the TUI/status path.';
693
+ statusPathLoginBootstrap.failedAt = null;
694
+ statusPathLoginBootstrap.promise = (async () => {
695
+ const loginSignal = AbortSignal.timeout(LOGIN_TIMEOUT_MS);
696
+ const loginStart = await startLoginFlow(loginSignal);
697
+ const browserOpenError = await openBrowser(loginStart.browserUrl);
698
+ if (browserOpenError) {
699
+ statusPathLoginBootstrap.message = `Automatic browser open failed. Open ${loginStart.browserUrl} manually.`;
700
+ }
701
+ try {
702
+ const callbackPayload = await loginStart.callbackPromise;
703
+ if (callbackPayload.status === 'error') {
704
+ throw new Error(callbackPayload.message);
705
+ }
706
+ if (callbackPayload.state !== loginStart.expectedState) {
707
+ throw new Error('OAuth callback state did not match the original login request.');
708
+ }
709
+ const pluginSession = await createPluginSession({
710
+ code: callbackPayload.code,
711
+ codeVerifier: loginStart.codeVerifier,
712
+ redirectUri: OIDC_CALLBACK_URL,
713
+ config,
714
+ signal: loginSignal
715
+ });
716
+ const authState = toAuthState(pluginSession);
717
+ await writeAuthState(path.resolve(worktree, config.authStatePath), authState);
718
+ statusPathLoginBootstrap.status = 'authenticated';
719
+ statusPathLoginBootstrap.message = `Browser login completed successfully for ${authState.email}.`;
720
+ return authState;
721
+ } finally {
722
+ await loginStart.closeCallbackServer().catch(() => undefined);
723
+ }
724
+ })().catch(error => {
725
+ statusPathLoginBootstrap.status = 'failed';
726
+ statusPathLoginBootstrap.failedAt = Date.now();
727
+ statusPathLoginBootstrap.message = error instanceof Error ? error.message : 'Browser login failed.';
728
+ throw error;
729
+ }).finally(() => {
730
+ statusPathLoginBootstrap.promise = null;
731
+ });
732
+ statusPathLoginBootstrap.promise.catch(() => undefined);
733
+ };
734
+ export const resolvePluginStatusSnapshotWithAuthBootstrap = async ({
735
+ worktree,
736
+ directory,
737
+ signal
738
+ }) => {
739
+ const snapshot = await resolvePluginStatusSnapshot({
740
+ worktree,
741
+ directory,
742
+ signal
743
+ });
744
+ if (snapshot.status !== 'missing_auth') return snapshot;
745
+ const config = await resolveConfig(worktree);
746
+ startStatusPathLoginBootstrap(worktree, config);
747
+ if (statusPathLoginBootstrap.message) {
748
+ return withStatusMessage(snapshot, statusPathLoginBootstrap.message);
749
+ }
750
+ return withStatusMessage(snapshot, 'Browser login is pending from the TUI/status path.');
751
+ };
675
752
  const toPluginStatusMetadata = snapshot => ({
676
753
  backendOrigin: snapshot.backendOrigin,
677
754
  graphqlUrl: snapshot.graphqlUrl,
@@ -1798,14 +1875,30 @@ const OpencodeWizardSkillsPlugin = async input => {
1798
1875
  })
1799
1876
  },
1800
1877
  'experimental.chat.system.transform': async (_hookInput, output) => {
1801
- const publishedSkillsResult = await loadPublishedSkillCatalog({
1878
+ let publishedSkillsResult = await loadPublishedSkillCatalog({
1802
1879
  directory: input.directory,
1803
1880
  useCache: true,
1804
1881
  signal: AbortSignal.timeout(5_000)
1805
1882
  });
1806
1883
  if (!publishedSkillsResult.fetchResult.ok && publishedSkillsResult.fetchResult.status === 'missing_auth') {
1807
- output.system.push('opencode-wizard plugin stored auth is missing, expired, or rejected. Passive startup will not open browser login; use opencode_wizard_status or opencode_wizard_published_skills_fetch interactively to authenticate when published skills are needed. No tokens are exposed.');
1808
- return;
1884
+ try {
1885
+ await startLoginCompletion('status').then(async authState => {
1886
+ await schedulePresenceStart(authState);
1887
+ });
1888
+ publishedSkillsResult = await loadPublishedSkillCatalog({
1889
+ directory: input.directory,
1890
+ useCache: false,
1891
+ signal: AbortSignal.timeout(5_000)
1892
+ });
1893
+ } catch {
1894
+ const loginMessage = loginBootstrap.snapshot.message ? ` Last login status: ${loginBootstrap.snapshot.message}` : '';
1895
+ output.system.push(`opencode-wizard plugin stored auth is missing, expired, or rejected. Startup browser login was started but did not complete successfully.${loginMessage} Use opencode_wizard_status or opencode_wizard_published_skills_fetch to retry authentication when published skills are needed. No tokens are exposed.`);
1896
+ return;
1897
+ }
1898
+ if (!publishedSkillsResult.fetchResult.ok) {
1899
+ output.system.push(`opencode-wizard plugin startup login completed, but published skills are still unavailable: ${publishedSkillsResult.fetchResult.message} No tokens are exposed.`);
1900
+ return;
1901
+ }
1809
1902
  }
1810
1903
  const details = await loadSystemNoteDetails({
1811
1904
  publishedSkillsResult,