@extension.dev/mcp 6.3.0 → 6.5.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.
Files changed (33) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +105 -18
  4. package/README.md +15 -2
  5. package/bin/extension-mcp.js +1 -0
  6. package/dist/module.js +3942 -2053
  7. package/dist/src/lib/artifacts-api.d.ts +56 -0
  8. package/dist/src/lib/carrier.d.ts +0 -57
  9. package/dist/src/lib/credentials.d.ts +0 -9
  10. package/dist/src/lib/device-flow.d.ts +0 -6
  11. package/dist/src/lib/github-device.d.ts +0 -9
  12. package/dist/src/lib/guest-load-oracle.d.ts +0 -10
  13. package/dist/src/lib/login-flow.d.ts +0 -36
  14. package/dist/src/lib/preview-upload.d.ts +33 -0
  15. package/dist/src/lib/project-ignore.d.ts +10 -0
  16. package/dist/src/lib/publish.d.ts +0 -10
  17. package/dist/src/lib/registry-access.d.ts +3 -19
  18. package/dist/src/lib/registry.d.ts +0 -65
  19. package/dist/src/lib/share-record.d.ts +33 -0
  20. package/dist/src/lib/vendor/chrome-theme/chrome-theme-color-math.d.ts +84 -0
  21. package/dist/src/lib/vendor/chrome-theme/chrome-theme-reference.d.ts +270 -0
  22. package/dist/src/lib/vendor/chrome-theme/chrome-theme-resolve.d.ts +52 -0
  23. package/dist/src/tools/open.d.ts +1 -0
  24. package/dist/src/tools/preview-web.d.ts +76 -0
  25. package/dist/src/tools/shares.d.ts +51 -0
  26. package/dist/src/tools/stop.d.ts +0 -1
  27. package/dist/src/tools/store-status.d.ts +0 -9
  28. package/dist/src/tools/theme-verify.d.ts +22 -0
  29. package/extensions/live-preview/chromium/action/index.js +9 -1
  30. package/extensions/live-preview/chromium/background/service_worker.js +10 -2
  31. package/extensions/live-preview/chromium/manifest.json +1 -0
  32. package/package.json +2 -2
  33. package/server.json +3 -3
@@ -0,0 +1,56 @@
1
+ type FetchImpl = typeof fetch;
2
+ export interface ListedArtifact {
3
+ artifactId: string;
4
+ kind?: string;
5
+ name?: string;
6
+ slug?: string;
7
+ version?: string;
8
+ live: boolean;
9
+ createdAt?: string;
10
+ expiresAt?: string | null;
11
+ revokedAt?: string | null;
12
+ sizeBytes?: number | null;
13
+ previewUrl?: string | null;
14
+ viewUrl?: string | null;
15
+ zipUrl?: string | null;
16
+ revokeUrl?: string;
17
+ }
18
+ export interface ArtifactListing {
19
+ artifacts: ListedArtifact[];
20
+ count: number;
21
+ matched: number;
22
+ limit: number;
23
+ truncated: boolean;
24
+ scanned: number;
25
+ }
26
+ export interface ArtifactRevocation {
27
+ artifactId: string;
28
+ revoked: boolean;
29
+ revokedAt?: string;
30
+ }
31
+ export type ArtifactsOutcome<T> = {
32
+ ok: true;
33
+ data: T;
34
+ } | {
35
+ ok: false;
36
+ error: {
37
+ name: string;
38
+ message: string;
39
+ status?: number;
40
+ };
41
+ };
42
+ export declare function parseArtifactRef(input: string): string | null;
43
+ export declare function listArtifacts(options?: {
44
+ limit?: number;
45
+ liveOnly?: boolean;
46
+ api?: string;
47
+ token?: string;
48
+ fetchImpl?: FetchImpl;
49
+ }): Promise<ArtifactsOutcome<ArtifactListing>>;
50
+ export declare function revokeArtifact(options: {
51
+ artifactId: string;
52
+ api?: string;
53
+ token?: string;
54
+ fetchImpl?: FetchImpl;
55
+ }): Promise<ArtifactsOutcome<ArtifactRevocation>>;
56
+ export {};
@@ -1,73 +1,21 @@
1
- /**
2
- * The Extension.dev Live Preview carrier, bundled prebuilt in this package
3
- * under extensions/live-preview/<engine>. extension_dev materializes it into
4
- * the project's ./extensions folder (which Extension.js auto-scans and loads
5
- * as a companion next to the user's extension), so the dev browser comes up
6
- * carrier-equipped: web pages the carrier allowlists (inspect.extension.dev,
7
- * localhost) can then watch the session's real-lane trace and pair with it.
8
- */
9
1
  export declare const CARRIER_DIR_NAME = "extension-dev-live-preview";
10
2
  export declare const CARRIER_EXTENSION_ID = "ibppeifnekhjjjmpjfiobccjlicbmgcb";
11
- /** Where the carrier lands inside a project. */
12
3
  export declare function carrierPath(projectPath: string): string;
13
- /** True only for a directory carrying our marker, i.e. ours to delete. */
14
4
  export declare function isManagedCarrier(projectPath: string): boolean;
15
5
  export type CarrierRemoval = {
16
6
  removed: boolean;
17
7
  path: string;
18
- /** Present when something was there but was NOT ours to touch. */
19
8
  note?: string;
20
9
  };
21
- /**
22
- * Remove the carrier from a project, marker-guarded.
23
- *
24
- * The trace swarm's most release-dangerous finding was that a debugging flag
25
- * leaves a permanent companion extension in the project: it survived
26
- * extension_stop, Extension.js auto-scans ./extensions, and the first
27
- * `git add -A` vendors it. Nothing removed it, ever. Stop and build both call
28
- * this now, and `extension_dev carrier: true` puts it back on demand.
29
- */
30
10
  export declare function removeCarrier(projectPath: string): CarrierRemoval;
31
- /**
32
- * Keep the carrier out of the user's commits. The scaffolder runs `git init`
33
- * with no initial commit, so the first `git add -A` in a project that ever ran
34
- * with carrier: true vendors 460K of somebody else's extension.
35
- * Returns the line added, or null when nothing needed doing.
36
- */
37
11
  export declare function ensureCarrierIgnored(projectPath: string): string | null;
38
12
  export type CarrierMaterialization = {
39
13
  loaded: boolean;
40
14
  path?: string;
41
15
  note: string;
42
- /** The .gitignore entry this run added, when it added one. */
43
16
  gitignored?: string;
44
- /**
45
- * What the carrier lane CANNOT do, stated at the moment it is handed over.
46
- * The trace swarm's top finding was that the real lane's boundaries were
47
- * discoverable only by experiment: 14 personas independently filed the
48
- * carrier-identity scoping as a severe bug, and 10 more expected the trace
49
- * to show their own extension's calls. Both are honest constraints; neither
50
- * was written down anywhere the caller would look.
51
- */
52
17
  limitations?: string[];
53
- /**
54
- * The graduation path, stated next to the shared-identity limitation so the
55
- * caller is not left at a dead end. The carrier lane is the SHARED real lane
56
- * (calls run as the carrier); but the guest is ALREADY loaded as itself in
57
- * this same session, so its OWN storage, identity and messaging are reached by
58
- * driving the guest directly with the control verbs, not the carrier bridge.
59
- * This is the agent-lane twin of the inspect Trace tab's "load as your own
60
- * extension" affordance: here the extension is already loaded, so graduating
61
- * means targeting it, not launching it.
62
- */
63
18
  graduation?: string;
64
- /**
65
- * How to actually DRIVE the real lane. Seven personas reached a
66
- * permanently empty trace and concluded the feature was broken, because
67
- * nothing in the 33 tool schemas, this note, or the rendered page named the
68
- * carrier's extension id or its message envelopes. Every persona that did
69
- * reach the real lane used out-of-band knowledge of the emulator source.
70
- */
71
19
  bridgeProtocol?: {
72
20
  carrierExtensionId: string;
73
21
  allowedOrigins: string;
@@ -75,9 +23,4 @@ export type CarrierMaterialization = {
75
23
  example: string;
76
24
  };
77
25
  };
78
- /**
79
- * Copy the bundled carrier into <projectPath>/extensions/<CARRIER_DIR_NAME>.
80
- * Refuses to touch an existing directory that lacks our marker file (it is
81
- * the user's, not ours); otherwise replaces it so version updates propagate.
82
- */
83
26
  export declare function materializeCarrier(projectPath: string, browser: string): CarrierMaterialization;
@@ -1,14 +1,10 @@
1
1
  export interface StoredCredentials {
2
2
  version: 1;
3
- /** The HMAC access token the publish path sends as `Bearer`. */
4
3
  token: string;
5
4
  workspaceSlug: string;
6
5
  projectSlug: string;
7
- /** Token expiry as unix epoch seconds (0 if unknown). */
8
6
  expiresAt: number;
9
- /** Platform base URL the token was minted against. */
10
7
  api: string;
11
- /** Which device flow minted this token: extension.dev-gated or GitHub-direct. */
12
8
  provider?: "extensiondev" | "github";
13
9
  }
14
10
  export declare function credentialsPath(): string;
@@ -18,9 +14,4 @@ export declare function clearCredentials(): {
18
14
  cleared: boolean;
19
15
  path: string;
20
16
  };
21
- /**
22
- * Return stored credentials only if the token has not expired. Used by the
23
- * publish token resolution so an expired local token falls through cleanly to
24
- * "no token" instead of producing a 401 from the platform.
25
- */
26
17
  export declare function readValidCredentials(nowSeconds?: number): StoredCredentials | null;
@@ -23,12 +23,6 @@ export type DevicePollResult = {
23
23
  reason: "pending" | "denied" | "expired" | "error";
24
24
  message?: string;
25
25
  };
26
- /**
27
- * Poll the extension.dev token endpoint until approved or the budget runs out.
28
- * The budget is kept under the MCP client request timeout; the tool re-invokes
29
- * with the same deviceCode to keep waiting (idempotent), mirroring the GitHub
30
- * poll's two-phase contract.
31
- */
32
26
  export declare function pollDeviceToken(args: {
33
27
  apiBase: string;
34
28
  path: string;
@@ -2,9 +2,7 @@ export interface DeviceCodeStart {
2
2
  deviceCode: string;
3
3
  userCode: string;
4
4
  verificationUri: string;
5
- /** Minimum seconds to wait between polls. */
6
5
  interval: number;
7
- /** Seconds until the device code expires. */
8
6
  expiresIn: number;
9
7
  }
10
8
  export type PollResult = {
@@ -22,13 +20,6 @@ export declare function startDeviceCode(args: {
22
20
  scope?: string;
23
21
  fetchImpl?: FetchImpl;
24
22
  }): Promise<DeviceCodeStart>;
25
- /**
26
- * Poll for the access token until the budget elapses. A budget shorter than
27
- * the device-code lifetime lets a caller (e.g. an MCP tool that must return
28
- * promptly) poll in bounded slices and report "pending" between calls; a long
29
- * budget (a blocking CLI) waits the whole time. Returns `pending` only on
30
- * budget exhaustion; `expired`/`denied` are terminal.
31
- */
32
23
  export declare function pollForToken(args: {
33
24
  clientId: string;
34
25
  deviceCode: string;
@@ -11,16 +11,6 @@ export type GuestLoadCheck = {
11
11
  cdpPort?: number;
12
12
  reason: string;
13
13
  };
14
- /**
15
- * The BUGS_TO_FIX §83 oracle: ask the BROWSER whether the guest actually loaded,
16
- * instead of trusting the engine's ready.json. Chrome refusing --load-extension
17
- * is invisible to every engine surface (ready.json stamps status:ready, empty
18
- * logs), so the only trustworthy "did the guest load" signal is a
19
- * chrome-extension:// target in the browser's own CDP /json list. The engine's
20
- * devtools companion is always present and is not the guest; a NON-companion
21
- * extension target is. Never throws: an unreachable endpoint returns
22
- * checked:false so callers can degrade instead of failing.
23
- */
24
14
  export declare function verifyGuestLoaded(projectPath: string, browser: string, options?: {
25
15
  waitMs?: number;
26
16
  timeoutMs?: number;
@@ -1,22 +1,7 @@
1
1
  import { type StoredCredentials } from "./credentials";
2
- /**
3
- * The platform clamps CLI token TTL to at most 7 days (server-owned; this
4
- * client only states the truth). Every token surface repeats it so CI authors
5
- * learn the rotation contract up front instead of from a 401 a week after
6
- * wiring the pipeline. Links the console's Access tokens page when the
7
- * project is known.
8
- */
9
2
  export declare function tokenTtlNote(workspaceSlug?: string, projectSlug?: string): string;
10
3
  type FetchImpl = typeof fetch;
11
4
  export declare function resolveApiBase(api?: string): string;
12
- /**
13
- * Validate the platform base URL BEFORE we attach a bearer token to a request to
14
- * it. SECURITY: the `api` arg / EXTENSION_DEV_API_URL is operator-supplied, but a
15
- * hostile value (e.g. via prompt-injection in the client) could redirect the
16
- * token to an attacker. The access token must never leave over plaintext or go to
17
- * an arbitrary scheme, so we require https -- allowing http only for localhost
18
- * dev. Returns the normalized base (no trailing slash) or an error message.
19
- */
20
5
  export declare function safeApiBase(raw: string): {
21
6
  ok: true;
22
7
  base: string;
@@ -25,10 +10,6 @@ export declare function safeApiBase(raw: string): {
25
10
  message: string;
26
11
  };
27
12
  export interface LoginConfig {
28
- /**
29
- * "extensiondev": the extension.dev-gated device flow (branded /device, GitHub
30
- * federated server-side). "github": the legacy GitHub-direct device flow.
31
- */
32
13
  provider: "extensiondev" | "github";
33
14
  clientId: string;
34
15
  scope: string;
@@ -36,29 +17,12 @@ export interface LoginConfig {
36
17
  deviceTokenUrl: string;
37
18
  verificationUri: string;
38
19
  }
39
- /**
40
- * Resolve how the device flow should authenticate. The server's public config
41
- * picks the provider (extension.dev-gated once its device endpoints are live,
42
- * else GitHub-direct). EXTENSION_DEV_GITHUB_CLIENT_ID forces the GitHub flow
43
- * (useful when pointing at a self-hosted platform).
44
- */
45
20
  export declare function fetchLoginConfig(apiBase: string, fetchImpl?: FetchImpl): Promise<LoginConfig>;
46
- /**
47
- * Persist a `{ token, expiresAt, workspaceSlug, projectSlug }` response (the
48
- * shape returned by BOTH the GitHub exchange endpoint and the extension.dev
49
- * device/token endpoint) to the local credentials file. Records which provider
50
- * minted it.
51
- */
52
21
  export declare function persistTokenResponse(args: {
53
22
  apiBase: string;
54
23
  data: Record<string, unknown>;
55
24
  provider: "extensiondev" | "github";
56
25
  }): StoredCredentials;
57
- /**
58
- * Trade a verified GitHub user token for a project-scoped access token and
59
- * write it to the local credentials file. Returns the stored credentials
60
- * (token included for the caller's in-memory use; never logged).
61
- */
62
26
  export declare function exchangeAndPersist(args: {
63
27
  apiBase: string;
64
28
  githubToken: string;
@@ -0,0 +1,33 @@
1
+ type FetchImpl = typeof fetch;
2
+ export interface PreviewUploadFile {
3
+ path: string;
4
+ content: string;
5
+ encoding: "utf8" | "base64";
6
+ }
7
+ export interface PreviewUploadResult {
8
+ artifactId: string;
9
+ previewUrl: string;
10
+ zipUrl?: string;
11
+ revokeUrl?: string;
12
+ expiresAt?: string;
13
+ }
14
+ export type PreviewUploadOutcome = {
15
+ ok: true;
16
+ data: PreviewUploadResult;
17
+ } | {
18
+ ok: false;
19
+ error: {
20
+ name: string;
21
+ message: string;
22
+ };
23
+ };
24
+ export declare function collectDistFiles(distDir: string): PreviewUploadFile[];
25
+ export declare function uploadPreview(options: {
26
+ distDir: string;
27
+ manifest: Record<string, unknown>;
28
+ browser: string;
29
+ api?: string;
30
+ token?: string;
31
+ fetchImpl?: FetchImpl;
32
+ }): Promise<PreviewUploadOutcome>;
33
+ export {};
@@ -0,0 +1,10 @@
1
+ export type ProjectIgnoreState = "added" | "already-ignored" | "not-a-repo" | "failed";
2
+ export interface ProjectIgnoreOutcome {
3
+ state: ProjectIgnoreState;
4
+ entry: string;
5
+ }
6
+ export declare function ensureProjectIgnored(projectPath: string, options: {
7
+ entry: string;
8
+ aliases?: string[];
9
+ comment: string;
10
+ }): ProjectIgnoreOutcome;
@@ -1,14 +1,9 @@
1
1
  type FetchImpl = typeof fetch;
2
- /** Resolve the access token: EXTENSION_DEV_TOKEN env first, then the login creds file. */
3
2
  export declare function resolveToken(): string;
4
3
  export interface PublishOptions {
5
- /** Share-link lifetime in hours (1-168, platform default 24). */
6
4
  ttlHours?: number;
7
- /** Pin the share URL to a specific build sha. */
8
5
  buildSha?: string;
9
- /** Platform base URL (defaults to https://www.extension.dev or EXTENSION_DEV_API_URL). */
10
6
  api?: string;
11
- /** Explicit token; defaults to resolveToken(). */
12
7
  token?: string;
13
8
  fetchImpl?: FetchImpl;
14
9
  }
@@ -22,10 +17,5 @@ export type PublishResult = {
22
17
  message: string;
23
18
  };
24
19
  };
25
- /**
26
- * Publish the project the token is scoped to and return the platform
27
- * response (shareUrl, visibility, ...). The publish target is identified by
28
- * the token's claims (workspaceSlug/projectSlug), so no project path is sent.
29
- */
30
20
  export declare function publish(options?: PublishOptions): Promise<PublishResult>;
31
21
  export {};
@@ -3,43 +3,27 @@ export type AccessGrant = {
3
3
  status: "ok";
4
4
  token: string;
5
5
  expiresAt: number;
6
- }
7
- /** Project is public; no token is needed and none was minted. */
8
- | {
6
+ } | {
9
7
  status: "public";
10
- }
11
- /** No stored credential, or it does not cover the requested project. */
12
- | {
8
+ } | {
13
9
  status: "no-credential";
14
- }
15
- /** Reached the platform and it refused (401/403/5xx, network, bad body). */
16
- | {
10
+ } | {
17
11
  status: "denied";
18
12
  httpStatus?: number;
19
13
  message: string;
20
14
  };
21
15
  export interface AccessTokenCacheOptions {
22
16
  fetchImpl?: typeof fetch;
23
- /** Test-only clock override, unix epoch seconds. */
24
17
  nowSeconds?: () => number;
25
18
  }
26
- /**
27
- * Per-process cache of minted tokens. Concurrent readers of the same project
28
- * share one in-flight mint: a single tool call fans out over meta.json,
29
- * channels.json and builds/index.json, and three cold 401s must not become
30
- * three mint round-trips.
31
- */
32
19
  export declare class RegistryAccessTokens {
33
20
  private readonly cache;
34
21
  private readonly fetchImpl;
35
22
  private readonly nowSeconds;
36
23
  constructor(options?: AccessTokenCacheOptions);
37
- /** A cached, still-valid token, or "" if none. Never mints. */
38
24
  peek(ref: ProjectRef): string;
39
25
  get(ref: ProjectRef, apiHint?: string): Promise<AccessGrant>;
40
26
  private mint;
41
27
  }
42
- /** Process-wide default, so one tool call's fan-out shares one mint. */
43
28
  export declare const defaultRegistryAccessTokens: RegistryAccessTokens;
44
- /** Append `?t=<token>` to a registry URL. */
45
29
  export declare function withAccessToken(url: string, token: string): string;
@@ -1,56 +1,19 @@
1
1
  import { type RegistryAccessTokens } from "./registry-access";
2
2
  import { type Origins } from "@extension.dev/urls/origins";
3
3
  export declare const REGISTRY_BASE_DEFAULT: string;
4
- /**
5
- * Resolve the fleet origins from the MCP's env vars, once per call. Console,
6
- * registry, and media all DERIVE from the platform base (`EXTENSION_DEV_API_URL`
7
- * or a per-tool `api`): point that at a local stack and every dashboard link the
8
- * MCP hands back follows to the dev host (`console.extension.localhost`) instead
9
- * of silently pointing at prod. Explicit per-host env vars still win. Falls back
10
- * to production when nothing is set. The dev-derivation lives in
11
- * `@extension.dev/urls` so it stays identical to what the apps resolve.
12
- */
13
4
  export declare function mcpOrigins(apiHint?: string): Origins;
14
- /** Base origin of the console dashboard, dev-aware (no trailing slash). */
15
5
  export declare function consoleBase(apiHint?: string): string;
16
6
  export declare function registryBase(): string;
17
7
  export interface ProjectRef {
18
8
  workspace: string;
19
9
  project: string;
20
10
  }
21
- /**
22
- * Resolve which project registry reads should target: explicit overrides
23
- * first, then the stored login's workspace/project slugs. Returns null when
24
- * neither names a project (e.g. token came from EXTENSION_DEV_TOKEN and no
25
- * login was ever stored).
26
- */
27
11
  export declare function resolveProjectRef(overrides?: {
28
12
  workspace?: string;
29
13
  project?: string;
30
14
  }): ProjectRef | null;
31
- /** URL of a file under the project's `_extension-dev/` registry directory. */
32
15
  export declare function registryFileUrl(ref: ProjectRef, file: string): string;
33
- /**
34
- * Console page URL for the project (builds, releases/new, stores, ...). The
35
- * origin is dev-aware; the path comes from the shared route contract so a
36
- * console route rename is caught in one place. Pass `apiHint` (a tool's `api`
37
- * arg) so the link matches that call's environment; otherwise it follows
38
- * `EXTENSION_DEV_API_URL`/`EXTENSION_DEV_CONSOLE_URL`.
39
- */
40
16
  export declare function consoleProjectUrl(ref: ProjectRef | null, page: string, apiHint?: string): string;
41
- /**
42
- * The PUBLIC page for a project, a build, or a channel on userland.
43
- *
44
- * The sibling of consoleProjectUrl, and the one to hand to a human who is not
45
- * the operator: the console links every tool already returns require a login
46
- * and workspace membership, so they are dead ends for a teammate, a reviewer,
47
- * or anyone reading a PR. These carry the per-browser downloads, the run
48
- * locally and integrity dialogs, and the what's new tab.
49
- *
50
- * A PRIVATE project's page still needs a `?share=` token, which only
51
- * extension_publish can mint; callers should say so rather than imply the bare
52
- * URL will open for anyone.
53
- */
54
17
  export declare function userlandProjectUrl(ref: ProjectRef | null, page?: string, apiHint?: string): string;
55
18
  export type RegistryFetchResult<T> = {
56
19
  ok: true;
@@ -60,19 +23,6 @@ export type RegistryFetchResult<T> = {
60
23
  status?: number;
61
24
  message: string;
62
25
  };
63
- /**
64
- * Fetch a registry JSON file. Never throws: 404s (never-built projects),
65
- * network failures, and non-JSON bodies all come back as `{ok:false}` so
66
- * callers can degrade honestly instead of crashing the verb.
67
- *
68
- * PRIVATE PROJECTS: a private project answers 401 here. When `ref` is given and
69
- * the caller holds a credential for that project, the 401 triggers one mint of
70
- * a short-lived access token and a single retry with `?t=`. Without `ref` the
71
- * behaviour is exactly as before, so this stays a drop-in for public reads:
72
- * public projects still complete in one round trip and never touch the
73
- * platform. See registry-access.ts for why the stored token is traded rather
74
- * than sent as `?t=` directly.
75
- */
76
26
  export declare function fetchRegistryJson<T = unknown>(url: string, fetchImpl?: typeof fetch, options?: {
77
27
  ref?: ProjectRef | null;
78
28
  api?: string;
@@ -83,16 +33,9 @@ export interface ChannelEntry {
83
33
  sha: string;
84
34
  buildId?: string;
85
35
  version?: string;
86
- /** ISO timestamp parsed from the channel row when the writer recorded one. */
87
36
  promotedAt?: string;
88
37
  description?: string;
89
38
  }
90
- /**
91
- * Normalize the registry's channels.json (a map of channel -> row) into a
92
- * list. `promotedAt` is not a first-class field in the file today; the
93
- * promote workflow stamps it into the description ("Promoted from X on
94
- * <ISO>"), so it is parsed back out when present.
95
- */
96
39
  export declare function parseChannels(json: unknown): ChannelEntry[];
97
40
  export interface BuildIndexItem {
98
41
  sha: string;
@@ -105,13 +48,5 @@ export interface BuildIndexItem {
105
48
  timestamp?: string;
106
49
  browsers?: string[];
107
50
  }
108
- /** Normalize `_extension-dev/builds/index.json` items (schemaVersion 3). */
109
51
  export declare function parseBuildIndex(json: unknown): BuildIndexItem[];
110
- /**
111
- * Derive the project mirror repo's Actions URL from any GitHub run URL the
112
- * registry exposes (stores/status.json carries one from the mirror's own
113
- * runs: https://github.com/extensiondev/<ownerGithubId>--<projectId>/actions/
114
- * runs/<id>). Only trusts the extensiondev org so a user-source-repo run URL
115
- * (build.json's runUrl) is never mistaken for the mirror.
116
- */
117
52
  export declare function mirrorActionsUrlFromRunUrl(runUrl: unknown): string | null;
@@ -0,0 +1,33 @@
1
+ import { type ProjectIgnoreState } from "./project-ignore";
2
+ export declare const SHARE_STATE_DIR = ".extension.dev";
3
+ export declare const SHARE_RECORD_FILE = "shared-previews.json";
4
+ export interface SharedPreviewEntry {
5
+ sharedAt: string;
6
+ previewUrl: string;
7
+ artifactId: string;
8
+ revokeUrl?: string;
9
+ expiresAt?: string;
10
+ zipUrl?: string;
11
+ name?: string;
12
+ version?: string;
13
+ browser?: string;
14
+ distDir?: string;
15
+ }
16
+ export interface ShareRecordOutcome {
17
+ recorded: boolean;
18
+ path: string;
19
+ entries?: number;
20
+ gitignored?: ProjectIgnoreState;
21
+ preserved?: string;
22
+ note: string;
23
+ warning?: string;
24
+ }
25
+ export declare function sharedPreviewsPath(projectPath: string): string;
26
+ export interface SharedPreviewsFile {
27
+ path: string;
28
+ exists: boolean;
29
+ unreadable: boolean;
30
+ entries: SharedPreviewEntry[];
31
+ }
32
+ export declare function readSharedPreviews(projectPath: string): SharedPreviewsFile;
33
+ export declare function recordSharedPreview(projectPath: string, entry: SharedPreviewEntry): ShareRecordOutcome;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Exact TypeScript ports of the Chromium color math used by the browser
3
+ * theme pipeline (ui/gfx/color_utils.cc, refs/heads/main, cached under
4
+ * parity/chromium-src-cache, fetched 2026-07-04).
5
+ *
6
+ * Every function mirrors its Chromium namesake so the theme resolver can
7
+ * reproduce Chrome's derived colors bit-for-bit. When editing, re-read the
8
+ * cited source instead of "fixing" the math: several rules (HSL lightness
9
+ * shift in RGB space, the luminance midpoint, alpha normalization) are
10
+ * deliberately not the textbook versions.
11
+ */
12
+ export interface RgbaColor {
13
+ r: number;
14
+ g: number;
15
+ b: number;
16
+ /** 0..255, like SkColor. */
17
+ a: number;
18
+ }
19
+ /** HSL tint triple; -1 in a channel means "leave unchanged". */
20
+ export interface HslTint {
21
+ h: number;
22
+ s: number;
23
+ l: number;
24
+ }
25
+ /** color_utils.cc:35 g_darkest_color = gfx::kGoogleGrey900. */
26
+ export declare const DARKEST_COLOR: RgbaColor;
27
+ /**
28
+ * color_utils.cc:40 g_luminance_midpoint: the luminance where white and
29
+ * kGoogleGrey900 contrast equally. IsDark() pivots here, NOT at 0.5.
30
+ */
31
+ export declare const LUMINANCE_MIDPOINT = 0.211692036;
32
+ /** color_utils.cc kMinimumReadableContrastRatio. */
33
+ export declare const MINIMUM_READABLE_CONTRAST_RATIO = 4.5;
34
+ export declare function rgba(r: number, g: number, b: number, a?: number): RgbaColor;
35
+ export declare function parseHexColor(hex: string): RgbaColor;
36
+ export declare function toHexColor(color: RgbaColor): string;
37
+ /** base::ClampRound for the 0..255 channel domain. */
38
+ export declare function clampRound(value: number): number;
39
+ /** color_utils.cc:425 GetRelativeLuminance4f. */
40
+ export declare function relativeLuminance(color: RgbaColor): number;
41
+ /** color_utils.cc:412 GetContrastRatio (luminance form). */
42
+ export declare function contrastRatioFromLuminance(a: number, b: number): number;
43
+ /** color_utils.cc:402 GetContrastRatio. */
44
+ export declare function contrastRatio(a: RgbaColor, b: RgbaColor): number;
45
+ /** color_utils.cc:617 IsDark: luminance below the grey900/white midpoint. */
46
+ export declare function isDarkChromium(color: RgbaColor): boolean;
47
+ /** color_utils.cc:621 GetColorWithMaxContrast: white or kGoogleGrey900. */
48
+ export declare function getColorWithMaxContrast(color: RgbaColor): RgbaColor;
49
+ /** color_utils.cc:625 GetEndpointColorWithMinContrast. */
50
+ export declare function getEndpointColorWithMinContrast(color: RgbaColor): RgbaColor;
51
+ /** color_utils.cc:436 SkColorToHSL. */
52
+ export declare function skColorToHsl(color: RgbaColor): HslTint;
53
+ /** color_utils.cc:467 HSLToSkColor. */
54
+ export declare function hslToSkColor(hsl: HslTint, alpha: number): RgbaColor;
55
+ /** color_utils.cc:518 MakeHSLShiftValid: out-of-range channels become -1. */
56
+ export declare function makeHslShiftValid(tint: HslTint): HslTint;
57
+ /** A tint that leaves the color unchanged. */
58
+ export declare const HSL_NO_TINT: HslTint;
59
+ /**
60
+ * color_utils.cc:534 HSLShift, Chrome's tint operation. NOT a plain HSL
61
+ * replacement: hue is replaced, saturation is scaled around 0.5, and the
62
+ * lightness shift happens in RGB space ("in the style of popular image
63
+ * editors").
64
+ */
65
+ export declare function hslShift(color: RgbaColor, shift: HslTint): RgbaColor;
66
+ /**
67
+ * color_utils.cc:581 AlphaBlend(float): weights each side by its own alpha
68
+ * times the blend factor (a foreground with alpha participates less).
69
+ */
70
+ export declare function alphaBlend(foreground: RgbaColor, background: RgbaColor, alpha: number): RgbaColor;
71
+ /** color_utils.cc:612 GetResultingPaintColor: composite fg over opaque bg. */
72
+ export declare function getResultingPaintColor(foreground: RgbaColor, background: RgbaColor): RgbaColor;
73
+ export interface BlendResult {
74
+ alpha: number;
75
+ color: RgbaColor;
76
+ }
77
+ /**
78
+ * color_utils.cc:647 BlendForMinContrast: binary-search the smallest blend
79
+ * of the target foreground into the default foreground that reaches
80
+ * `targetContrastRatio` against `background`.
81
+ */
82
+ export declare function blendForMinContrast(defaultForeground: RgbaColor, background: RgbaColor, highContrastForeground?: RgbaColor, targetContrastRatio?: number): BlendResult;
83
+ /** browser_theme_pack.cc:605 IsColorGrayscale: channel range under 9. */
84
+ export declare function isColorGrayscale(color: RgbaColor): boolean;