@extension.dev/mcp 6.6.0 → 7.0.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/dist/module.js CHANGED
@@ -629,7 +629,7 @@ __webpack_require__.d(whoami_namespaceObject, {
629
629
  handler: ()=>whoami_handler,
630
630
  schema: ()=>whoami_schema
631
631
  });
632
- var package_namespaceObject = JSON.parse('{"rE":"6.5.0","El":{"OP":"4.0.16-canary.1784889479.74e12044"}}');
632
+ var package_namespaceObject = JSON.parse('{"rE":"7.0.0","El":{"OP":"4.0.16-canary.1784889479.74e12044"}}');
633
633
  function credentialsPath() {
634
634
  if ("win32" === process.platform) {
635
635
  const base = process.env.APPDATA || process.env.LOCALAPPDATA || node_path.join(node_os.homedir(), "AppData", "Roaming");
@@ -647,7 +647,7 @@ function readCredentials() {
647
647
  if (1 !== data.version) return null;
648
648
  const token = String(data.token || "").trim();
649
649
  if (!token) return null;
650
- const provider = "extensiondev" === data.provider || "github" === data.provider ? data.provider : void 0;
650
+ const provider = "extensiondev" === data.provider ? data.provider : void 0;
651
651
  return {
652
652
  version: 1,
653
653
  token,
@@ -735,15 +735,6 @@ function safeApiBase(raw) {
735
735
  };
736
736
  }
737
737
  async function fetchLoginConfig(apiBase, fetchImpl = fetch) {
738
- const override = String(process.env.EXTENSION_DEV_GITHUB_CLIENT_ID || "").trim();
739
- if (override) return {
740
- provider: "github",
741
- clientId: override,
742
- scope: "read:user",
743
- deviceCodeUrl: "/api/cli/device/code",
744
- deviceTokenUrl: "/api/cli/device/token",
745
- verificationUri: "https://github.com/login/device"
746
- };
747
738
  const res = await fetchImpl(`${apiBase}/api/cli/login/config`, {
748
739
  headers: {
749
740
  accept: "application/json"
@@ -751,16 +742,10 @@ async function fetchLoginConfig(apiBase, fetchImpl = fetch) {
751
742
  });
752
743
  if (!res.ok) throw new Error(`Could not fetch login config from ${apiBase} (${res.status}).`);
753
744
  const data = await res.json().catch(()=>({}));
754
- const provider = "extensiondev" === data.provider ? "extensiondev" : "github";
755
- const clientId = String(data.githubClientId || "").trim();
756
- if ("github" === provider && !clientId) throw new Error("Login is not configured on the server (no GitHub client id). Set EXTENSION_DEV_GITHUB_CLIENT_ID to override.");
757
745
  return {
758
- provider,
759
- clientId,
760
- scope: String(data.scope || "read:user"),
761
746
  deviceCodeUrl: String(data.deviceCodeUrl || "/api/cli/device/code"),
762
747
  deviceTokenUrl: String(data.deviceTokenUrl || "/api/cli/device/token"),
763
- verificationUri: String(data.verificationUri || "https://github.com/login/device")
748
+ verificationUri: String(data.verificationUri || `${apiBase.replace(/\/+$/, "")}/device`)
764
749
  };
765
750
  }
766
751
  function persistTokenResponse(args) {
@@ -773,40 +758,11 @@ function persistTokenResponse(args) {
773
758
  projectSlug: String(args.data.projectSlug || ""),
774
759
  expiresAt: Number(args.data.expiresAt || 0),
775
760
  api: args.apiBase,
776
- provider: args.provider
761
+ provider: "extensiondev"
777
762
  };
778
763
  writeCredentials(creds);
779
764
  return creds;
780
765
  }
781
- async function exchangeAndPersist(args) {
782
- const doFetch = args.fetchImpl ?? fetch;
783
- const res = await doFetch(`${args.apiBase}/api/cli/login/exchange`, {
784
- method: "POST",
785
- headers: {
786
- "content-type": "application/json",
787
- accept: "application/json"
788
- },
789
- body: JSON.stringify({
790
- githubToken: args.githubToken,
791
- project: args.project
792
- })
793
- });
794
- const text = await res.text();
795
- let data;
796
- try {
797
- data = JSON.parse(text);
798
- } catch {
799
- data = {
800
- message: text
801
- };
802
- }
803
- if (!res.ok) throw new Error(`Login exchange failed (${res.status}): ${data.message || "unknown error"}`);
804
- return persistTokenResponse({
805
- apiBase: args.apiBase,
806
- data,
807
- provider: "github"
808
- });
809
- }
810
766
  function _define_property(obj, key, value) {
811
767
  if (key in obj) Object.defineProperty(obj, key, {
812
768
  value: value,
@@ -2006,7 +1962,7 @@ function materializeCarrier(projectPath, browser) {
2006
1962
  ...ignored ? {
2007
1963
  gitignored: ignored
2008
1964
  } : {},
2009
- note: "Live-preview carrier placed in ./extensions; Extension.js loads it as a companion beside your extension. Open https://inspect.extension.dev/?session=live in the dev browser (any http://localhost origin works too) to watch the session's real-lane chrome.* trace on the Trace tab. It is a debug companion, never part of a release: extension_stop and extension_build remove it again" + (ignored ? `, and ${ignored} was added to .gitignore.` : "."),
1965
+ note: "Live-preview carrier placed in ./extensions; Extension.js loads it as a companion beside your extension. Open https://preview.extension.dev/?session=live in the dev browser (any http://localhost origin works too) to watch the session's real-lane chrome.* trace on the Trace tab. It is a debug companion, never part of a release: extension_stop and extension_build remove it again" + (ignored ? `, and ${ignored} was added to .gitignore.` : "."),
2010
1966
  limitations: [
2011
1967
  "The trace shows calls a PAGE bridges to the carrier. Your extension's own chrome.* calls run directly in its contexts and never cross the carrier, so they do not appear.",
2012
1968
  "Bridged calls run under the CARRIER's identity, not your extension's. The preview assumes a single active guest and does not namespace per-extension state, so storage, action/badge state, messaging delivery, offscreen documents and relative script paths belong to the carrier. Rows affected are badged carrier-scoped in the Trace tab.",
@@ -2016,7 +1972,7 @@ function materializeCarrier(projectPath, browser) {
2016
1972
  ...carrierId ? {
2017
1973
  bridgeProtocol: {
2018
1974
  carrierExtensionId: carrierId,
2019
- allowedOrigins: "https://inspect.extension.dev, https://intelligence.extension.dev, https://themes.extension.dev, http://localhost/*, http://127.0.0.1/*",
1975
+ allowedOrigins: "https://preview.extension.dev, https://intelligence.extension.dev, https://themes.extension.dev, http://localhost/*, http://127.0.0.1/*",
2020
1976
  howTo: "From a page on an allowed origin, register your guest once with a 'session' message (it declares the permissions the carrier enforces), then send 'bridge' messages to run chrome.* for real; each one streams into the Trace tab. Use the EXACT dotted wire names the bridge dispatcher accepts: storage is storage.get/set/remove/clear with the AREA AS AN ARGUMENT, NOT storage.local.get.",
2021
1977
  example: [
2022
1978
  `const id = '${carrierId}'`,
@@ -2693,7 +2649,7 @@ const dev_schema = {
2693
2649
  carrier: {
2694
2650
  type: "boolean",
2695
2651
  default: false,
2696
- description: "Load the bundled Extension.dev Live Preview carrier beside your extension (Chromium-family browsers only). It is placed in the project's ./extensions folder, which Extension.js auto-loads; allowlisted pages (inspect.extension.dev, localhost) can then pair with the session and stream its real-lane chrome.* trace. Writes extensions/extension-dev-live-preview/ into the project, gitignores it, and takes it back out on extension_stop or extension_build: it is a debug companion, never part of a release."
2652
+ description: "Load the bundled Extension.dev Live Preview carrier beside your extension (Chromium-family browsers only). It is placed in the project's ./extensions folder, which Extension.js auto-loads; allowlisted pages (preview.extension.dev, localhost) can then pair with the session and stream its real-lane chrome.* trace. Writes extensions/extension-dev-live-preview/ into the project, gitignores it, and takes it back out on extension_stop or extension_build: it is a debug companion, never part of a release."
2697
2653
  }
2698
2654
  },
2699
2655
  required: [
@@ -4573,23 +4529,13 @@ function recordSharedPreview(projectPath, entry) {
4573
4529
  } : {}
4574
4530
  };
4575
4531
  }
4576
- const DEFAULT_INSPECT_URL = "http://localhost:3106";
4577
4532
  const DEFAULT_PREVIEW_DEV_URL = "http://localhost:3110";
4578
- const SURFACES = {
4579
- preview: {
4580
- defaultOrigin: DEFAULT_PREVIEW_DEV_URL,
4581
- scheme: (encoded)=>`preview://build/${encoded}`,
4582
- fetchPath: "/__preview/fetch",
4583
- devCommand: "pnpm --filter preview.extension.dev dev",
4584
- label: "preview.extension.dev"
4585
- },
4586
- inspect: {
4587
- defaultOrigin: DEFAULT_INSPECT_URL,
4588
- scheme: (encoded)=>`inspect://path/${encoded}`,
4589
- fetchPath: "/__inspect/fetch",
4590
- devCommand: "pnpm --filter inspect.extension.dev dev",
4591
- label: "inspect.extension.dev"
4592
- }
4533
+ const SURFACE = {
4534
+ defaultOrigin: DEFAULT_PREVIEW_DEV_URL,
4535
+ scheme: (encoded)=>`preview://build/${encoded}`,
4536
+ fetchPath: "/__preview/fetch",
4537
+ devCommand: "pnpm --filter preview.extension.dev dev",
4538
+ label: "preview.extension.dev"
4593
4539
  };
4594
4540
  async function buildShare(projectPath, distDir, manifest, browser) {
4595
4541
  const result = await uploadPreview({
@@ -4674,7 +4620,7 @@ function detectSurfaces(manifest) {
4674
4620
  }
4675
4621
  const preview_web_schema = {
4676
4622
  name: "extension_preview_web",
4677
- description: "Preview an in-progress extension in the web emulator (no real browser). Builds the project (unless build:false), points preview.extension.dev at the dist directory over the dev-only preview://build scheme, and returns a deep link plus a loadability check against its dev server. preview is the author's front door: it renders YOUR build and carries the Emulated/Real lane toggle and the Trace tab. Pass surface:\"inspect\" to render in inspect.extension.dev instead (the evaluator's door, for fixture and forensic work). Pass share:true to UPLOAD the dist you just built and get back a public link (share.previewUrl) that renders those exact bytes for anyone who opens it, with no install and no dev server, and that also serves the whole build as a downloadable zip. Sharing needs a token scoped to an existing extension.dev workspace and project (extension_login or EXTENSION_DEV_TOKEN, valid up to 7 days), so a local folder with no project on the platform cannot share. Use share:true whenever the build has to reach someone who is not at this machine, since the plain deepLink only resolves locally. Shared links do not disappear when this response scrolls away: extension_shares lists every link this token has shared and revokes any of them.",
4623
+ description: "Preview an in-progress extension in the web emulator (no real browser). Builds the project (unless build:false), points preview.extension.dev at the dist directory over the dev-only preview://build scheme, and returns a deep link plus a loadability check against its dev server. preview.extension.dev is the author's door for a local build: it renders YOUR build and carries the Emulated/Real lane toggle and the Trace tab. Pass share:true to UPLOAD the dist you just built and get back a public link (share.previewUrl) that renders those exact bytes for anyone who opens it, with no install and no dev server, and that also serves the whole build as a downloadable zip. Sharing needs a token scoped to an existing extension.dev workspace and project (extension_login or EXTENSION_DEV_TOKEN, valid up to 7 days), so a local folder with no project on the platform cannot share. Use share:true whenever the build has to reach someone who is not at this machine, since the plain deepLink only resolves locally. Shared links do not disappear when this response scrolls away: extension_shares lists every link this token has shared and revokes any of them.",
4678
4624
  inputSchema: {
4679
4625
  type: "object",
4680
4626
  properties: {
@@ -4698,7 +4644,7 @@ const preview_web_schema = {
4698
4644
  "safari"
4699
4645
  ],
4700
4646
  default: "chrome",
4701
- description: "Which dist/<browser> output to preview. inspect renders it in the mocked-Chrome emulator regardless."
4647
+ description: "Which dist/<browser> output to preview. The emulator renders it as mocked Chrome regardless."
4702
4648
  },
4703
4649
  build: {
4704
4650
  type: "boolean",
@@ -4709,22 +4655,9 @@ const preview_web_schema = {
4709
4655
  type: "string",
4710
4656
  description: "Preview this built directory directly instead of resolving dist/<browser> under projectPath. Implies build:false."
4711
4657
  },
4712
- surface: {
4713
- type: "string",
4714
- enum: [
4715
- "preview",
4716
- "inspect"
4717
- ],
4718
- default: "preview",
4719
- description: "Which front door renders the build. \"preview\" (default) is preview.extension.dev, the author's door: your own in-progress build, with the Emulated/Real lane toggle and the Trace tab. \"inspect\" is inspect.extension.dev, the evaluator's door."
4720
- },
4721
4658
  hostUrl: {
4722
4659
  type: "string",
4723
- description: "Origin of the running dev server for the chosen surface (defaults: http://localhost:3110 for preview, http://localhost:3106 for inspect)."
4724
- },
4725
- inspectUrl: {
4726
- type: "string",
4727
- description: "Deprecated alias for hostUrl, kept for callers written before the preview surface existed. Only consulted when surface is \"inspect\"."
4660
+ description: "Origin of the running preview.extension.dev dev server (default http://localhost:3110)."
4728
4661
  },
4729
4662
  probe: {
4730
4663
  type: "boolean",
@@ -4766,9 +4699,7 @@ const preview_web_schema = {
4766
4699
  };
4767
4700
  async function preview_web_handler(args) {
4768
4701
  const browser = args.browser ?? "chrome";
4769
- const surfaceKey = "inspect" === args.surface ? "inspect" : "preview";
4770
- const surface = SURFACES[surfaceKey];
4771
- const hostBase = (args.hostUrl ?? ("inspect" === surfaceKey ? args.inspectUrl : void 0) ?? surface.defaultOrigin).replace(/\/+$/, "");
4702
+ const hostBase = (args.hostUrl ?? SURFACE.defaultOrigin).replace(/\/+$/, "");
4772
4703
  const shouldBuild = args.distPath ? false : false !== args.build;
4773
4704
  let buildResult = null;
4774
4705
  if (shouldBuild) {
@@ -4811,12 +4742,11 @@ async function preview_web_handler(args) {
4811
4742
  });
4812
4743
  }
4813
4744
  const encoded = Buffer.from(distDir).toString("base64url");
4814
- const internalUrl = surface.scheme(encoded);
4745
+ const internalUrl = SURFACE.scheme(encoded);
4815
4746
  const deepLink = `${hostBase}/?url=${encodeURIComponent(internalUrl)}`;
4816
4747
  const result = {
4817
4748
  ok: true,
4818
4749
  deepLink,
4819
- surface: surfaceKey,
4820
4750
  distDir,
4821
4751
  manifest: {
4822
4752
  name: manifest.name ?? node_path.basename(distDir),
@@ -4829,7 +4759,7 @@ async function preview_web_handler(args) {
4829
4759
  } : {
4830
4760
  built: false
4831
4761
  },
4832
- hint: `Open deepLink in a browser to see the extension render in ${surface.label}'s emulator. It must be running (${surface.devCommand}).${"preview" === surfaceKey ? " Once it renders, the Trace tab shows every chrome.* call it makes, and the lane toggle switches between the emulated backend and a real carrier-equipped browser." : ""}`
4762
+ hint: `Open deepLink in a browser to see the extension render in ${SURFACE.label}'s emulator. It must be running (${SURFACE.devCommand}). Once it renders, the Trace tab shows every chrome.* call it makes, and the lane toggle switches between the emulated backend and a real carrier-equipped browser.`
4833
4763
  };
4834
4764
  if (args.open) {
4835
4765
  const sessionBrowser = args.openIn ?? browser;
@@ -4849,7 +4779,7 @@ async function preview_web_handler(args) {
4849
4779
  }
4850
4780
  if (args.share) result.share = await buildShare(args.projectPath, distDir, manifest, browser);
4851
4781
  if (false === args.probe) return JSON.stringify(result);
4852
- const probeUrl = `${hostBase}${surface.fetchPath}?url=${encodeURIComponent(internalUrl)}`;
4782
+ const probeUrl = `${hostBase}${SURFACE.fetchPath}?url=${encodeURIComponent(internalUrl)}`;
4853
4783
  try {
4854
4784
  const res = await fetch(probeUrl, {
4855
4785
  headers: {
@@ -4864,7 +4794,7 @@ async function preview_web_handler(args) {
4864
4794
  probe: {
4865
4795
  status: res.status,
4866
4796
  contentType,
4867
- note: `${surface.label} answered but not with a preview payload. On the deployed host ${surface.fetchPath} does not exist (dev-only); run a local dev server (${surface.devCommand}) to use web preview.`
4797
+ note: `${SURFACE.label} answered but not with a preview payload. On the deployed host ${SURFACE.fetchPath} does not exist (dev-only); run a local dev server (${SURFACE.devCommand}) to use web preview.`
4868
4798
  }
4869
4799
  });
4870
4800
  const payload = await res.json();
@@ -4886,7 +4816,7 @@ async function preview_web_handler(args) {
4886
4816
  previewLoadable: false,
4887
4817
  probe: {
4888
4818
  error: err instanceof Error ? err.message : String(err),
4889
- note: `Could not reach ${surface.label} at ${hostBase}. Start it with '${surface.devCommand}', then open deepLink.`
4819
+ note: `Could not reach ${SURFACE.label} at ${hostBase}. Start it with '${SURFACE.devCommand}', then open deepLink.`
4890
4820
  }
4891
4821
  });
4892
4822
  }
@@ -10066,78 +9996,6 @@ async function add_feature_handler(args) {
10066
9996
  hint: conflicts.length ? `Warning: ${conflicts.length} file(s) already exist and would be overwritten.` : "No conflicts detected. Safe to create all files."
10067
9997
  });
10068
9998
  }
10069
- const GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
10070
- const GITHUB_TOKEN_URL = "https://github.com/login/oauth/access_token";
10071
- const DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
10072
- const defaultSleep = (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
10073
- async function startDeviceCode(args) {
10074
- const doFetch = args.fetchImpl ?? fetch;
10075
- const res = await doFetch(GITHUB_DEVICE_CODE_URL, {
10076
- method: "POST",
10077
- headers: {
10078
- accept: "application/json",
10079
- "content-type": "application/json"
10080
- },
10081
- body: JSON.stringify({
10082
- client_id: args.clientId,
10083
- scope: args.scope || "read:user"
10084
- })
10085
- });
10086
- const data = await res.json().catch(()=>({}));
10087
- if (!res.ok || data.error) throw new Error(`GitHub device-code request failed: ${data.error_description || data.error || res.status}`);
10088
- return {
10089
- deviceCode: String(data.device_code || ""),
10090
- userCode: String(data.user_code || ""),
10091
- verificationUri: String(data.verification_uri || "https://github.com/login/device"),
10092
- interval: Number(data.interval || 5),
10093
- expiresIn: Number(data.expires_in || 900)
10094
- };
10095
- }
10096
- async function pollForToken(args) {
10097
- const doFetch = args.fetchImpl ?? fetch;
10098
- const sleep = args.sleepImpl ?? defaultSleep;
10099
- let intervalMs = 1000 * Math.max(1, args.interval);
10100
- const deadline = Date.now() + Math.max(0, args.budgetMs);
10101
- while(Date.now() < deadline){
10102
- await sleep(intervalMs);
10103
- const res = await doFetch(GITHUB_TOKEN_URL, {
10104
- method: "POST",
10105
- headers: {
10106
- accept: "application/json",
10107
- "content-type": "application/json"
10108
- },
10109
- body: JSON.stringify({
10110
- client_id: args.clientId,
10111
- device_code: args.deviceCode,
10112
- grant_type: DEVICE_GRANT_TYPE
10113
- })
10114
- });
10115
- const data = await res.json().catch(()=>({}));
10116
- const token = String(data.access_token || "").trim();
10117
- if (token) return {
10118
- ok: true,
10119
- githubToken: token
10120
- };
10121
- const error = String(data.error || "");
10122
- if ("authorization_pending" === error) continue;
10123
- if ("slow_down" === error) {
10124
- intervalMs = Math.max(intervalMs + 5000, 1000 * Number(data.interval || 0));
10125
- continue;
10126
- }
10127
- if ("expired_token" === error) return {
10128
- ok: false,
10129
- reason: "expired"
10130
- };
10131
- if ("access_denied" === error) return {
10132
- ok: false,
10133
- reason: "denied"
10134
- };
10135
- }
10136
- return {
10137
- ok: false,
10138
- reason: "pending"
10139
- };
10140
- }
10141
9999
  async function requestDeviceCode(args) {
10142
10000
  const doFetch = args.fetchImpl ?? fetch;
10143
10001
  const res = await doFetch(`${args.apiBase}${args.path}`, {
@@ -10201,8 +10059,7 @@ async function pollDeviceToken(args) {
10201
10059
  if (res.ok && data.token) {
10202
10060
  const creds = persistTokenResponse({
10203
10061
  apiBase: args.apiBase,
10204
- data,
10205
- provider: "extensiondev"
10062
+ data
10206
10063
  });
10207
10064
  return {
10208
10065
  ok: true,
@@ -10233,7 +10090,7 @@ async function pollDeviceToken(args) {
10233
10090
  }
10234
10091
  const login_schema = {
10235
10092
  name: "extension_login",
10236
- description: "Authenticate to extension.dev and store a project-scoped access token locally so extension_publish can use it. Two-phase: call with `project` to get a code + URL for the user to authorize, then call again with the returned `deviceCode` to finish. The server picks the flow: the extension.dev-gated device flow (you authorize at extension.dev/device; GitHub federation happens server-side, no GitHub token on this machine) or, as a fallback, the legacy GitHub device flow. Never returns the token. Minted tokens live at most 7 days (server-enforced), so CI pipelines must re-mint before expiry on the console's Access tokens page (project settings -> Access tokens). This is the only tool besides extension_publish that talks to the hosted platform.",
10093
+ description: "Authenticate to extension.dev and store a project-scoped access token locally so extension_publish can use it. Two-phase: call with `project` to get a code + URL for the user to authorize, then call again with the returned `deviceCode` to finish. You authorize at extension.dev/device; GitHub federation happens server-side, so no GitHub token ever lands on this machine. Never returns the token. Minted tokens live at most 7 days (server-enforced), so CI pipelines must re-mint before expiry on the console's Access tokens page (project settings -> Access tokens). This is the only tool besides extension_publish that talks to the hosted platform.",
10237
10094
  inputSchema: {
10238
10095
  type: "object",
10239
10096
  properties: {
@@ -10315,99 +10172,47 @@ async function login_handler(args) {
10315
10172
  } catch (err) {
10316
10173
  return login_fail("LoginConfigError", err?.message || "Could not load login config.");
10317
10174
  }
10318
- if ("extensiondev" === config.provider) {
10319
- if (args.deviceCode) {
10320
- const poll = await pollDeviceToken({
10321
- apiBase,
10322
- path: config.deviceTokenUrl,
10323
- project,
10324
- deviceCode: String(args.deviceCode),
10325
- interval: 5,
10326
- budgetMs: RESUME_BUDGET_MS
10327
- });
10328
- if (poll.ok) return success(poll.creds);
10329
- if ("expired" === poll.reason) return login_fail("LoginExpired", "The device code expired. Run extension_login again to restart.");
10330
- if ("denied" === poll.reason) return login_fail("LoginDenied", "Authorization was denied at extension.dev/device.");
10331
- if ("error" === poll.reason) return login_fail("LoginError", poll.message || "Device login failed.");
10332
- return resumePending(String(args.deviceCode), config.verificationUri);
10333
- }
10334
- let start;
10335
- try {
10336
- start = await requestDeviceCode({
10337
- apiBase,
10338
- path: config.deviceCodeUrl,
10339
- project
10340
- });
10341
- } catch (err) {
10342
- return login_fail("LoginStartError", err?.message || "Could not start the device flow.");
10343
- }
10175
+ if (args.deviceCode) {
10344
10176
  const poll = await pollDeviceToken({
10345
10177
  apiBase,
10346
10178
  path: config.deviceTokenUrl,
10347
10179
  project,
10348
- deviceCode: start.deviceCode,
10349
- interval: start.interval,
10350
- budgetMs: FIRST_CALL_BUDGET_MS
10351
- });
10352
- if (poll.ok) return success(poll.creds);
10353
- if ("denied" === poll.reason) return login_fail("LoginDenied", "Authorization was denied at extension.dev/device.");
10354
- return login_pending({
10355
- deviceCode: start.deviceCode,
10356
- userCode: start.userCode,
10357
- verificationUri: start.verificationUri,
10358
- verificationUriComplete: start.verificationUriComplete
10359
- });
10360
- }
10361
- if (args.deviceCode) {
10362
- const poll = await pollForToken({
10363
- clientId: config.clientId,
10364
10180
  deviceCode: String(args.deviceCode),
10365
10181
  interval: 5,
10366
10182
  budgetMs: RESUME_BUDGET_MS
10367
10183
  });
10368
- if (!poll.ok) {
10369
- if ("expired" === poll.reason) return login_fail("LoginExpired", "The device code expired. Run extension_login again to restart.");
10370
- if ("denied" === poll.reason) return login_fail("LoginDenied", "Authorization was denied on GitHub.");
10371
- return resumePending(String(args.deviceCode), "https://github.com/login/device");
10372
- }
10373
- try {
10374
- const creds = await exchangeAndPersist({
10375
- apiBase,
10376
- githubToken: poll.githubToken,
10377
- project
10378
- });
10379
- return success(creds);
10380
- } catch (err) {
10381
- return login_fail("LoginExchangeError", err?.message || "Token exchange failed.");
10382
- }
10184
+ if (poll.ok) return success(poll.creds);
10185
+ if ("expired" === poll.reason) return login_fail("LoginExpired", "The device code expired. Run extension_login again to restart.");
10186
+ if ("denied" === poll.reason) return login_fail("LoginDenied", "Authorization was denied at extension.dev/device.");
10187
+ if ("error" === poll.reason) return login_fail("LoginError", poll.message || "Device login failed.");
10188
+ return resumePending(String(args.deviceCode), config.verificationUri);
10383
10189
  }
10384
10190
  let start;
10385
10191
  try {
10386
- start = await startDeviceCode({
10387
- clientId: config.clientId,
10388
- scope: config.scope
10192
+ start = await requestDeviceCode({
10193
+ apiBase,
10194
+ path: config.deviceCodeUrl,
10195
+ project
10389
10196
  });
10390
10197
  } catch (err) {
10391
- return login_fail("LoginStartError", err?.message || "Could not start device flow.");
10198
+ return login_fail("LoginStartError", err?.message || "Could not start the device flow.");
10392
10199
  }
10393
- const poll = await pollForToken({
10394
- clientId: config.clientId,
10200
+ const poll = await pollDeviceToken({
10201
+ apiBase,
10202
+ path: config.deviceTokenUrl,
10203
+ project,
10395
10204
  deviceCode: start.deviceCode,
10396
10205
  interval: start.interval,
10397
10206
  budgetMs: FIRST_CALL_BUDGET_MS
10398
10207
  });
10399
- if (poll.ok) try {
10400
- const creds = await exchangeAndPersist({
10401
- apiBase,
10402
- githubToken: poll.githubToken,
10403
- project
10404
- });
10405
- return success(creds);
10406
- } catch (err) {
10407
- return login_fail("LoginExchangeError", err?.message || "Token exchange failed.");
10408
- }
10409
- if (!poll.ok && "denied" === poll.reason) return login_fail("LoginDenied", "Authorization was denied on GitHub.");
10410
- return login_pending(start);
10208
+ if (poll.ok) return success(poll.creds);
10209
+ if ("denied" === poll.reason) return login_fail("LoginDenied", "Authorization was denied at extension.dev/device.");
10210
+ return login_pending({
10211
+ deviceCode: start.deviceCode,
10212
+ userCode: start.userCode,
10213
+ verificationUri: start.verificationUri,
10214
+ verificationUriComplete: start.verificationUriComplete
10215
+ });
10411
10216
  }
10412
10217
  const whoami_schema = {
10413
10218
  name: "extension_whoami",
@@ -10443,7 +10248,7 @@ async function whoami_handler() {
10443
10248
  apiRecordedAtLogin: recordedApi
10444
10249
  } : {},
10445
10250
  apiDefault: effectiveDefaultApi,
10446
- provider: creds.provider ?? "github",
10251
+ provider: creds.provider ?? "extensiondev",
10447
10252
  expiresAt: creds.expiresAt ? new Date(1000 * creds.expiresAt).toISOString() : null,
10448
10253
  expiresInSeconds: creds.expiresAt ? creds.expiresAt - now : null,
10449
10254
  expired,
@@ -11279,55 +11084,28 @@ async function runCli(cmd, args) {
11279
11084
  const apiBase = resolveApiBase(flag("api"));
11280
11085
  try {
11281
11086
  const config = await fetchLoginConfig(apiBase);
11282
- if ("extensiondev" === config.provider) {
11283
- const start = await requestDeviceCode({
11284
- apiBase,
11285
- path: config.deviceCodeUrl,
11286
- project
11287
- });
11288
- log("");
11289
- log(` Open ${start.verificationUri} and enter code: ${start.userCode}`);
11290
- log("");
11291
- log(" Waiting for authorization...");
11292
- const poll = await pollDeviceToken({
11293
- apiBase,
11294
- path: config.deviceTokenUrl,
11295
- project,
11296
- deviceCode: start.deviceCode,
11297
- interval: start.interval,
11298
- budgetMs: 1000 * start.expiresIn
11299
- });
11300
- if (!poll.ok) {
11301
- log("denied" === poll.reason ? "Authorization was denied at extension.dev/device." : "expired" === poll.reason ? "The device code expired. Run login again." : "Timed out waiting for authorization. Run login again.");
11302
- return 1;
11303
- }
11304
- log(`Logged in to ${poll.creds.workspaceSlug}/${poll.creds.projectSlug}.`);
11305
- return 0;
11306
- }
11307
- const start = await startDeviceCode({
11308
- clientId: config.clientId,
11309
- scope: config.scope
11087
+ const start = await requestDeviceCode({
11088
+ apiBase,
11089
+ path: config.deviceCodeUrl,
11090
+ project
11310
11091
  });
11311
11092
  log("");
11312
11093
  log(` Open ${start.verificationUri} and enter code: ${start.userCode}`);
11313
11094
  log("");
11314
11095
  log(" Waiting for authorization...");
11315
- const poll = await pollForToken({
11316
- clientId: config.clientId,
11096
+ const poll = await pollDeviceToken({
11097
+ apiBase,
11098
+ path: config.deviceTokenUrl,
11099
+ project,
11317
11100
  deviceCode: start.deviceCode,
11318
11101
  interval: start.interval,
11319
11102
  budgetMs: 1000 * start.expiresIn
11320
11103
  });
11321
11104
  if (!poll.ok) {
11322
- log("denied" === poll.reason ? "Authorization was denied on GitHub." : "Timed out waiting for authorization. Run login again.");
11105
+ log("denied" === poll.reason ? "Authorization was denied at extension.dev/device." : "expired" === poll.reason ? "The device code expired. Run login again." : "Timed out waiting for authorization. Run login again.");
11323
11106
  return 1;
11324
11107
  }
11325
- const creds = await exchangeAndPersist({
11326
- apiBase,
11327
- githubToken: poll.githubToken,
11328
- project
11329
- });
11330
- log(`Logged in to ${creds.workspaceSlug}/${creds.projectSlug}.`);
11108
+ log(`Logged in to ${poll.creds.workspaceSlug}/${poll.creds.projectSlug}.`);
11331
11109
  return 0;
11332
11110
  } catch (err) {
11333
11111
  log(err instanceof Error ? err.message : String(err));
@@ -5,7 +5,7 @@ export interface StoredCredentials {
5
5
  projectSlug: string;
6
6
  expiresAt: number;
7
7
  api: string;
8
- provider?: "extensiondev" | "github";
8
+ provider?: "extensiondev";
9
9
  }
10
10
  export declare function credentialsPath(): string;
11
11
  export declare function readCredentials(): StoredCredentials | null;
@@ -10,9 +10,6 @@ export declare function safeApiBase(raw: string): {
10
10
  message: string;
11
11
  };
12
12
  export interface LoginConfig {
13
- provider: "extensiondev" | "github";
14
- clientId: string;
15
- scope: string;
16
13
  deviceCodeUrl: string;
17
14
  deviceTokenUrl: string;
18
15
  verificationUri: string;
@@ -21,12 +18,5 @@ export declare function fetchLoginConfig(apiBase: string, fetchImpl?: FetchImpl)
21
18
  export declare function persistTokenResponse(args: {
22
19
  apiBase: string;
23
20
  data: Record<string, unknown>;
24
- provider: "extensiondev" | "github";
25
21
  }): StoredCredentials;
26
- export declare function exchangeAndPersist(args: {
27
- apiBase: string;
28
- githubToken: string;
29
- project: string;
30
- fetchImpl?: FetchImpl;
31
- }): Promise<StoredCredentials>;
32
22
  export {};
@@ -23,20 +23,10 @@ export declare const schema: {
23
23
  type: string;
24
24
  description: string;
25
25
  };
26
- surface: {
27
- type: string;
28
- enum: string[];
29
- default: string;
30
- description: string;
31
- };
32
26
  hostUrl: {
33
27
  type: string;
34
28
  description: string;
35
29
  };
36
- inspectUrl: {
37
- type: string;
38
- description: string;
39
- };
40
30
  probe: {
41
31
  type: string;
42
32
  default: boolean;
@@ -66,9 +56,7 @@ export declare function handler(args: {
66
56
  browser?: string;
67
57
  build?: boolean;
68
58
  distPath?: string;
69
- surface?: string;
70
59
  hostUrl?: string;
71
- inspectUrl?: string;
72
60
  probe?: boolean;
73
61
  open?: boolean;
74
62
  openIn?: string;