@unbound-app/cli 1.1.0 → 1.1.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/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
- #!/usr/bin/env node
2
- import { A as readIcuMessage, E as writeLocaleTypings, F as formatDiagnostic, G as LOCALE_CODE_PATTERN, H as withoutFullStop, I as isDiagnostic, K as SOURCE_LOCALE, M as findFile, O as buildReportSchema, P as diagnostic, U as listAddonFiles, V as sortDiagnostics, W as LOCALES_DIRECTORY, X as ASSET_SCALES, Y as ASSET_NAME_PATTERN, Z as ASSET_TYPES, _ as formatBytes, at as __require, b as LOCALE_TYPINGS, et as ADDON_KINDS, j as IcuSyntaxError, k as readIcuArguments, nt as __commonJSMin, p as BUILD_REPORT_ENV, q as isLocaleCode, st as __toESM, t as SETTINGS_TYPINGS, y as LOCALE_MODULE } from "./settings-typings-CnJ5F-rP.mjs";
1
+ #!/usr/bin/env bun
2
+ import { $ as ASSET_NAME_PATTERN, A as readIcuMessage, E as writeLocaleTypings, F as formatDiagnostic, H as withoutFullStop, I as isDiagnostic, J as LOCALES_DIRECTORY, M as findFile, O as buildReportSchema, P as diagnostic, U as bytecodeFileName, V as sortDiagnostics, X as SOURCE_LOCALE, Y as LOCALE_CODE_PATTERN, Z as isLocaleCode, _ as formatBytes, at as __commonJSMin, b as LOCALE_TYPINGS, ct as __require, et as ASSET_SCALES, it as ADDON_KINDS, j as IcuSyntaxError, k as readIcuArguments, p as BUILD_REPORT_ENV, q as listAddonFiles, t as SETTINGS_TYPINGS, tt as ASSET_TYPES, ut as __toESM, y as LOCALE_MODULE } from "./settings-typings-BRlzDlLI.mjs";
3
3
  import { cli, command } from "cleye";
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
  import { CONTROLLER_ENDPOINT_MARKER, DEFAULT_BRIDGE_PORT, DEFAULT_EVAL_TIMEOUT_MS, MAX_EVAL_TIMEOUT_MS } from "@unbound-app/debugger-protocol";
6
6
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7
7
  import { ControllerClient } from "@unbound-app/debugger-protocol/controller";
8
- import { Type, Type as Type$1 } from "typebox";
8
+ import { Type, Type as Type$2 } from "typebox";
9
9
  import { Compile } from "typebox/compile";
10
10
  import { Format } from "typebox/format";
11
11
  import { Clone } from "typebox/value";
@@ -13,18 +13,18 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
13
13
  import blockScoping from "@babel/plugin-transform-block-scoping";
14
14
  import { transformSync } from "@babel/core";
15
15
  import { execFile, spawn, spawnSync } from "node:child_process";
16
- import { basename, delimiter, dirname, extname, join, relative, resolve, sep } from "node:path";
17
16
  import { closeSync, existsSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, watch, writeFileSync } from "node:fs";
18
- import { createHash } from "node:crypto";
17
+ import { availableParallelism, homedir, tmpdir } from "node:os";
18
+ import { basename, delimiter, dirname, extname, join, relative, resolve, sep } from "node:path";
19
19
  import { readdir, stat } from "node:fs/promises";
20
+ import { promisify } from "node:util";
21
+ import { createHash } from "node:crypto";
20
22
  import ts from "typescript-api";
21
23
  import pc from "picocolors";
22
24
  import { Visitor, parseSync } from "rolldown/utils";
23
25
  import { Lexer, walkTokens } from "marked";
24
26
  import { rgbaToThumbHash } from "thumbhash";
25
- import { promisify } from "node:util";
26
27
  import sharp from "sharp";
27
- import { availableParallelism, homedir, tmpdir } from "node:os";
28
28
  import { gzipSync } from "node:zlib";
29
29
  import MagicString from "magic-string";
30
30
  import { connect } from "node:net";
@@ -386,14 +386,14 @@ function safeParse(schema, value) {
386
386
  * @returns A schema that decodes to `value` when the field is missing.
387
387
  */
388
388
  function defaulted(schema, value) {
389
- return Type$1.Unsafe(Type$1.Decode(Type$1.Optional({
389
+ return Type$2.Unsafe(Type$2.Decode(Type$2.Optional({
390
390
  ...schema,
391
391
  default: value
392
392
  }), (encoded) => encoded ?? value));
393
393
  }
394
394
  // src/lib/context.ts
395
395
  /** The `port` field of every command that talks to the bridge; the CLI adapter dials the port it holds. */
396
- const bridgePortField = defaulted(Type$1.Number({ description: "The port the debugger bridge listens on." }), DEFAULT_BRIDGE_PORT);
396
+ const bridgePortField = defaulted(Type$2.Number({ description: "The port the debugger bridge listens on." }), DEFAULT_BRIDGE_PORT);
397
397
  /** How long a one-shot command waits for the bridge to report a device before giving up. */
398
398
  const DEVICE_WAIT_TIMEOUT_MS = 2e3;
399
399
  /** How often the device-wait polls the controller's connection state. */
@@ -733,50 +733,50 @@ function summary(report, found) {
733
733
  return parts.length ? `(${parts.join(", ")})` : "";
734
734
  }
735
735
  // src/lib/inspect-report.ts
736
- const platformSchema = Type$1.Enum(["android", "ios"]);
737
- const classReportSchema = Type$1.Object({
736
+ const platformSchema = Type$2.Enum(["android", "ios"]);
737
+ const classReportSchema = Type$2.Object({
738
738
  platform: platformSchema,
739
- name: Type$1.String(),
740
- superclasses: Type$1.Array(Type$1.String()),
741
- interfaces: Type$1.Array(Type$1.String()),
742
- constructors: Type$1.Array(Type$1.Array(Type$1.String())),
743
- methods: Type$1.Array(Type$1.Object({
744
- name: Type$1.String(),
745
- parameters: Type$1.Array(Type$1.String()),
746
- returnType: Type$1.String(),
747
- static: Type$1.Boolean()
739
+ name: Type$2.String(),
740
+ superclasses: Type$2.Array(Type$2.String()),
741
+ interfaces: Type$2.Array(Type$2.String()),
742
+ constructors: Type$2.Array(Type$2.Array(Type$2.String())),
743
+ methods: Type$2.Array(Type$2.Object({
744
+ name: Type$2.String(),
745
+ parameters: Type$2.Array(Type$2.String()),
746
+ returnType: Type$2.String(),
747
+ static: Type$2.Boolean()
748
748
  })),
749
- fields: Type$1.Array(Type$1.Object({
750
- name: Type$1.String(),
751
- type: Type$1.String(),
752
- static: Type$1.Boolean()
749
+ fields: Type$2.Array(Type$2.Object({
750
+ name: Type$2.String(),
751
+ type: Type$2.String(),
752
+ static: Type$2.Boolean()
753
753
  })),
754
- members: Type$1.Boolean()
754
+ members: Type$2.Boolean()
755
755
  });
756
- const viewRecordSchema = Type$1.Object({
757
- depth: Type$1.Number(),
758
- className: Type$1.String(),
759
- id: Type$1.Union([Type$1.String(), Type$1.Null()]),
760
- width: Type$1.Number(),
761
- height: Type$1.Number(),
762
- visibility: Type$1.Enum([
756
+ const viewRecordSchema = Type$2.Object({
757
+ depth: Type$2.Number(),
758
+ className: Type$2.String(),
759
+ id: Type$2.Union([Type$2.String(), Type$2.Null()]),
760
+ width: Type$2.Number(),
761
+ height: Type$2.Number(),
762
+ visibility: Type$2.Enum([
763
763
  "visible",
764
764
  "invisible",
765
765
  "gone",
766
766
  "hidden"
767
767
  ]),
768
- text: Type$1.Union([Type$1.String(), Type$1.Null()]),
769
- handle: Type$1.Union([Type$1.String(), Type$1.Null()])
768
+ text: Type$2.Union([Type$2.String(), Type$2.Null()]),
769
+ handle: Type$2.Union([Type$2.String(), Type$2.Null()])
770
770
  });
771
- const viewsReportSchema = Type$1.Object({
771
+ const viewsReportSchema = Type$2.Object({
772
772
  platform: platformSchema,
773
- roots: Type$1.Array(Type$1.Array(viewRecordSchema)),
774
- visited: Type$1.Union([Type$1.Number(), Type$1.Null()]),
775
- truncated: Type$1.Boolean()
773
+ roots: Type$2.Array(Type$2.Array(viewRecordSchema)),
774
+ visited: Type$2.Union([Type$2.Number(), Type$2.Null()]),
775
+ truncated: Type$2.Boolean()
776
776
  });
777
777
  /** What `inspectNative` answers with on the device, as `ubd inspect` reads it back. */
778
- const inspectReportSchema = Type$1.Union([
779
- Type$1.Object({ error: Type$1.String() }),
778
+ const inspectReportSchema = Type$2.Union([
779
+ Type$2.Object({ error: Type$2.String() }),
780
780
  classReportSchema,
781
781
  viewsReportSchema
782
782
  ]);
@@ -1092,25 +1092,25 @@ const inspectCommand = {
1092
1092
  cli: "root",
1093
1093
  mcp: true
1094
1094
  },
1095
- schema: Type$1.Object({
1096
- what: Type$1.Enum([
1095
+ schema: Type$2.Object({
1096
+ what: Type$2.Enum([
1097
1097
  "class",
1098
1098
  "views",
1099
1099
  "find"
1100
1100
  ], { description: "What to inspect: class, views or find." }),
1101
- name: defaulted(Type$1.String({ description: "The class to describe (class) or to look for (find)." }), ""),
1102
- class: defaulted(Type$1.String({ description: "For views: dump only the subtrees of attached views of this class." }), ""),
1103
- depth: defaulted(Type$1.Integer({
1101
+ name: defaulted(Type$2.String({ description: "The class to describe (class) or to look for (find)." }), ""),
1102
+ class: defaulted(Type$2.String({ description: "For views: dump only the subtrees of attached views of this class." }), ""),
1103
+ depth: defaulted(Type$2.Integer({
1104
1104
  minimum: 1,
1105
1105
  description: "For views: how deep to print."
1106
1106
  }), 40),
1107
- max: defaulted(Type$1.Integer({
1107
+ max: defaulted(Type$2.Integer({
1108
1108
  minimum: 1,
1109
1109
  description: "How many views to visit at most."
1110
1110
  }), 2e3),
1111
- filter: defaulted(Type$1.String({ description: "For class: keep only constructors, methods and fields whose name contains this." }), ""),
1111
+ filter: defaulted(Type$2.String({ description: "For class: keep only constructors, methods and fields whose name contains this." }), ""),
1112
1112
  port: bridgePortField,
1113
- timeout: defaulted(Type$1.Integer({
1113
+ timeout: defaulted(Type$2.Integer({
1114
1114
  minimum: 1,
1115
1115
  maximum: MAX_EVAL_TIMEOUT_MS,
1116
1116
  description: "How long the device may take, in milliseconds."
@@ -1349,7 +1349,7 @@ const MANIFEST_FILENAME = "manifest.json";
1349
1349
  /** The file `ubd install` leaves in an installed addon's folder so the client treats it as a development copy. */
1350
1350
  const DEVELOPMENT_MARKER = "development";
1351
1351
  // ../contracts/src/manifest.ts
1352
- const semverSchema = Type$1.String({
1352
+ const semverSchema = Type$2.String({
1353
1353
  maxLength: 64,
1354
1354
  pattern: `^${SEMVER_PATTERN}$`,
1355
1355
  errorMessage: "Must be a semver version."
@@ -1358,43 +1358,43 @@ const semverSchema = Type$1.String({
1358
1358
  * Addon ids become object-storage path segments (`addons/{id}/{version}/...`), so they are limited to
1359
1359
  * path-safe characters: alphanumeric runs joined by single `.`, `_` or `-`.
1360
1360
  */
1361
- const addonIdSchema = Type$1.String({
1361
+ const addonIdSchema = Type$2.String({
1362
1362
  maxLength: 100,
1363
1363
  pattern: ADDON_ID_PATTERN,
1364
1364
  errorMessage: "Must be alphanumeric segments joined by \".\", \"_\" or \"-\"."
1365
1365
  });
1366
- const addonMainSchema = Type$1.String({
1366
+ const addonMainSchema = Type$2.String({
1367
1367
  pattern: ADDON_MAIN_PATTERN,
1368
1368
  errorMessage: "Must be a relative path inside the addon."
1369
1369
  });
1370
1370
  /** Maps each Hermes bytecode version an addon ships to the SHA-512 of that compiled bundle. */
1371
- const bytecodeDigestsSchema = Type$1.Record(Type$1.String({
1371
+ const bytecodeDigestsSchema = Type$2.Record(Type$2.String({
1372
1372
  pattern: "^\\d{1,10}$",
1373
1373
  errorMessage: "Must be a Hermes bytecode version."
1374
- }), Type$1.String({
1374
+ }), Type$2.String({
1375
1375
  pattern: "^[\\da-f]{128}$",
1376
1376
  errorMessage: "Must be a lowercase SHA-512 digest."
1377
1377
  }), { additionalProperties: false });
1378
1378
  /** Maps each locale a plugin ships beside its bundle, other than en-US, to the SHA-256 of that `i18n/<code>.json`. */
1379
- const localeDigestsSchema = Type$1.Record(Type$1.String({
1379
+ const localeDigestsSchema = Type$2.Record(Type$2.String({
1380
1380
  pattern: LOCALE_CODE_PATTERN,
1381
1381
  errorMessage: "Must be a Discord locale code."
1382
- }), Type$1.String({
1382
+ }), Type$2.String({
1383
1383
  pattern: "^[\\da-f]{64}$",
1384
1384
  errorMessage: "Must be a lowercase SHA-256 digest."
1385
1385
  }), { additionalProperties: false });
1386
1386
  /** One image a plugin ships: its type, its size in points at scale 1, and the SHA-256 of its file at each scale. */
1387
- const addonAssetSchema = Type$1.Object({
1388
- type: Type$1.Enum(ASSET_TYPES),
1389
- width: Type$1.Integer({
1387
+ const addonAssetSchema = Type$2.Object({
1388
+ type: Type$2.Enum(ASSET_TYPES),
1389
+ width: Type$2.Integer({
1390
1390
  minimum: 1,
1391
1391
  maximum: 16384
1392
1392
  }),
1393
- height: Type$1.Integer({
1393
+ height: Type$2.Integer({
1394
1394
  minimum: 1,
1395
1395
  maximum: 16384
1396
1396
  }),
1397
- scales: Type$1.Record(Type$1.Enum(ASSET_SCALES), Type$1.String({
1397
+ scales: Type$2.Record(Type$2.Enum(ASSET_SCALES), Type$2.String({
1398
1398
  pattern: "^[\\da-f]{64}$",
1399
1399
  errorMessage: "Must be a lowercase SHA-256 digest."
1400
1400
  }), {
@@ -1403,46 +1403,46 @@ const addonAssetSchema = Type$1.Object({
1403
1403
  })
1404
1404
  }, { additionalProperties: false });
1405
1405
  /** Maps each image a plugin ships, by asset name, to its {@link addonAssetSchema} entry. */
1406
- const addonAssetsSchema = Type$1.Record(Type$1.String({
1406
+ const addonAssetsSchema = Type$2.Record(Type$2.String({
1407
1407
  pattern: ASSET_NAME_PATTERN,
1408
1408
  maxLength: 200,
1409
1409
  errorMessage: "Must be an asset name."
1410
1410
  }), addonAssetSchema, { additionalProperties: false });
1411
- const addonPlatformsSchema = Type$1.Refine(Type$1.Object({
1412
- ios: Type$1.Optional(Type$1.Object({ capabilities: Type$1.Optional(Type$1.Array(Type$1.Enum(NATIVE_PLUGIN_PLATFORM_CAPABILITIES.ios))) }, { additionalProperties: false })),
1413
- android: Type$1.Optional(Type$1.Object({ capabilities: Type$1.Optional(Type$1.Array(Type$1.Enum(NATIVE_PLUGIN_PLATFORM_CAPABILITIES.android))) }, { additionalProperties: false }))
1411
+ const addonPlatformsSchema = Type$2.Refine(Type$2.Object({
1412
+ ios: Type$2.Optional(Type$2.Object({ capabilities: Type$2.Optional(Type$2.Array(Type$2.Enum(NATIVE_PLUGIN_PLATFORM_CAPABILITIES.ios))) }, { additionalProperties: false })),
1413
+ android: Type$2.Optional(Type$2.Object({ capabilities: Type$2.Optional(Type$2.Array(Type$2.Enum(NATIVE_PLUGIN_PLATFORM_CAPABILITIES.android))) }, { additionalProperties: false }))
1414
1414
  }, { additionalProperties: false }), (platforms) => Object.keys(platforms).length > 0, () => "Must list a platform.");
1415
- const addonAuthorSchema = Type$1.Object({
1416
- name: Type$1.String({ minLength: 1 }),
1417
- id: Type$1.Unsafe(Type$1.String({ pattern: "^\\d+$" }))
1415
+ const addonAuthorSchema = Type$2.Object({
1416
+ name: Type$2.String({ minLength: 1 }),
1417
+ id: Type$2.Unsafe(Type$2.String({ pattern: "^\\d+$" }))
1418
1418
  });
1419
1419
  /**
1420
1420
  * The manifest an author publishes. Unknown keys pass through untouched, so the stored manifest is
1421
1421
  * what the author shipped; only the fields the platform relies on are validated.
1422
1422
  */
1423
- const addonManifestSchema = Type$1.Object({
1423
+ const addonManifestSchema = Type$2.Object({
1424
1424
  id: addonIdSchema,
1425
- type: Type$1.Optional(Type$1.Enum(ADDON_KINDS)),
1426
- name: Type$1.String({ minLength: 1 }),
1427
- description: Type$1.String(),
1428
- authors: Type$1.Array(addonAuthorSchema, { minItems: 1 }),
1429
- icon: Type$1.Optional(Type$1.Union([Type$1.String(), Type$1.Object({ uri: Type$1.String() })])),
1425
+ type: Type$2.Optional(Type$2.Enum(ADDON_KINDS)),
1426
+ name: Type$2.String({ minLength: 1 }),
1427
+ description: Type$2.String(),
1428
+ authors: Type$2.Array(addonAuthorSchema, { minItems: 1 }),
1429
+ icon: Type$2.Optional(Type$2.Union([Type$2.String(), Type$2.Object({ uri: Type$2.String() })])),
1430
1430
  main: addonMainSchema,
1431
1431
  version: semverSchema,
1432
1432
  /** The platforms the addon runs on, each with its capabilities; absent means every platform. */
1433
- platforms: Type$1.Optional(addonPlatformsSchema),
1434
- capabilities: Type$1.Optional(Type$1.Array(Type$1.Enum(NATIVE_PLUGIN_CAPABILITIES))),
1435
- minNativePluginApi: Type$1.Optional(Type$1.String()),
1436
- unbound: Type$1.Optional(Type$1.Object({ min: Type$1.Optional(semverSchema) })),
1437
- bytecode: Type$1.Optional(bytecodeDigestsSchema),
1433
+ platforms: Type$2.Optional(addonPlatformsSchema),
1434
+ capabilities: Type$2.Optional(Type$2.Array(Type$2.Enum(NATIVE_PLUGIN_CAPABILITIES))),
1435
+ minNativePluginApi: Type$2.Optional(Type$2.String()),
1436
+ unbound: Type$2.Optional(Type$2.Object({ min: Type$2.Optional(semverSchema) })),
1437
+ bytecode: Type$2.Optional(bytecodeDigestsSchema),
1438
1438
  /** The locale files `ubd build` emits beside the bundle, by code; en-US is in the bundle. */
1439
- locales: Type$1.Optional(localeDigestsSchema),
1439
+ locales: Type$2.Optional(localeDigestsSchema),
1440
1440
  /** The images `ubd build` emits beside the bundle, by asset name. */
1441
- assets: Type$1.Optional(addonAssetsSchema),
1441
+ assets: Type$2.Optional(addonAssetsSchema),
1442
1442
  /** Marks a plugin other plugins build on, which the plugin list hides by default. */
1443
- library: Type$1.Optional(Type$1.Boolean()),
1443
+ library: Type$2.Optional(Type$2.Boolean()),
1444
1444
  /** The addons this one needs, by id, each with the semver range it accepts. */
1445
- dependencies: Type$1.Optional(Type$1.Record(addonIdSchema, Type$1.String({ minLength: 1 }), { additionalProperties: false }))
1445
+ dependencies: Type$2.Optional(Type$2.Record(addonIdSchema, Type$2.String({ minLength: 1 }), { additionalProperties: false }))
1446
1446
  }, { additionalProperties: true });
1447
1447
  // src/lib/device-paths.ts
1448
1448
  /** Discord's bundle id on iOS. */
@@ -3516,17 +3516,17 @@ var import_picomatch = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((
3516
3516
  /** The workspace config's filename; the directory holding one is a workspace root. */
3517
3517
  const CONFIG_FILENAME = "unbound.config.json";
3518
3518
  /** The validated shape of an `unbound.config.json`. */
3519
- const workspaceConfigSchema = Type$1.Object({
3520
- $schema: Type$1.Optional(Type$1.String()),
3521
- addons: Type$1.Array(Type$1.String(), { default: ["plugins/*", "themes/*"] }),
3522
- build: Type$1.String({ default: "bun run build" }),
3523
- output: Type$1.String({ default: "dist" })
3519
+ const workspaceConfigSchema = Type$2.Object({
3520
+ $schema: Type$2.Optional(Type$2.String()),
3521
+ addons: Type$2.Array(Type$2.String(), { default: ["plugins/*", "themes/*"] }),
3522
+ build: Type$2.String({ default: "bun run build" }),
3523
+ output: Type$2.String({ default: "dist" })
3524
3524
  });
3525
- const manifestFieldsSchema = Type$1.Object({
3526
- id: Type$1.String(),
3527
- main: Type$1.String()
3525
+ const manifestFieldsSchema = Type$2.Object({
3526
+ id: Type$2.String(),
3527
+ main: Type$2.String()
3528
3528
  });
3529
- const packageScriptsSchema = Type$1.Object({ scripts: Type$1.Optional(Type$1.Object({ build: Type$1.Optional(Type$1.String()) }, { additionalProperties: true })) });
3529
+ const packageScriptsSchema = Type$2.Object({ scripts: Type$2.Optional(Type$2.Object({ build: Type$2.Optional(Type$2.String()) }, { additionalProperties: true })) });
3530
3530
  /**
3531
3531
  * @description Picks, out of the directories holding a `manifest.json`, the ones the workspace registers.
3532
3532
  * @param config The workspace config.
@@ -3663,9 +3663,9 @@ function toError(error) {
3663
3663
  return error instanceof Error ? error : new Error(String(error));
3664
3664
  }
3665
3665
  // src/lib/manifest.ts
3666
- const devManifestSchema = Type$1.Unsafe(Type$1.Object({
3667
- id: Type$1.String(),
3668
- main: Type$1.String()
3666
+ const devManifestSchema = Type$2.Unsafe(Type$2.Object({
3667
+ id: Type$2.String(),
3668
+ main: Type$2.String()
3669
3669
  }, { additionalProperties: true }));
3670
3670
  function readWith(dir, schema) {
3671
3671
  const path = join(dir, MANIFEST_FILENAME);
@@ -3789,24 +3789,32 @@ function sha512Digests(compiled) {
3789
3789
  // src/lib/plugin-install.ts
3790
3790
  const SAFE_ADDON_ID = /^[\w.-]+$/;
3791
3791
  /**
3792
- * @description Lists the built files a plugin install copies onto a device: its manifest, bundle and the extra files the manifest lists.
3792
+ * @description Lists the built files a plugin install copies onto a device: its manifest, bundle, Hermes bytecode and the
3793
+ * extra files the manifest lists. The manifest copied names each bytecode file's digest, which the loader checks before it
3794
+ * runs the bytecode instead of compiling the source.
3793
3795
  * @param addon The built plugin.
3794
3796
  * @returns The plugin's id and files, or why its id cannot be used as a folder name on the device.
3795
3797
  */
3796
3798
  function readPluginPayload(addon) {
3797
3799
  if (!SAFE_ADDON_ID.test(addon.id)) return { error: `${addon.id} is not a valid plugin id for a device folder.` };
3798
- const { manifest } = readBuiltAddon(addon);
3800
+ const { manifest, bytecode = {} } = readBuiltAddon(addon);
3801
+ const bundle = getBundleName(addon.static, manifest.main);
3799
3802
  const names = [
3800
- MANIFEST_FILENAME,
3801
- getBundleName(addon.static, manifest.main),
3803
+ bundle,
3804
+ ...Object.keys(bytecode).map((version) => bytecodeFileName(bundle, Number(version))),
3802
3805
  ...listAddonFiles(manifest).map((file) => file.path)
3803
3806
  ];
3807
+ const staged = join(mkdtempSync(join(tmpdir(), "ubd-install-")), MANIFEST_FILENAME);
3808
+ writeFileSync(staged, JSON.stringify(manifest, null, " "));
3804
3809
  return {
3805
3810
  id: addon.id,
3806
- files: names.map((name) => ({
3811
+ files: [{
3812
+ name: MANIFEST_FILENAME,
3813
+ path: staged
3814
+ }, ...names.map((name) => ({
3807
3815
  name,
3808
3816
  path: join(addon.output, name)
3809
- }))
3817
+ }))]
3810
3818
  };
3811
3819
  }
3812
3820
  /**
@@ -12248,51 +12256,51 @@ function splitFrontmatter(source) {
12248
12256
  // ../contracts/src/addon-page.ts
12249
12257
  const UNDOTTED_SEGMENT = String.raw`[\w-][\w.-]*`;
12250
12258
  const MEDIA_PATH = new RegExp(String.raw`^media/(?:${UNDOTTED_SEGMENT}/)*${UNDOTTED_SEGMENT}$`);
12251
- const mediaPathSchema = Type$1.Refine(Type$1.String({
12259
+ const mediaPathSchema = Type$2.Refine(Type$2.String({
12252
12260
  maxLength: 200,
12253
12261
  pattern: MEDIA_PATH,
12254
12262
  errorMessage: "Must be a relative path inside the media/ folder."
12255
12263
  }), (path) => getMediaKind(path) !== void 0, () => `Must end in ${Object.keys(MEDIA_EXTENSIONS).join(", ")}.`);
12256
- const imagePathSchema = Type$1.Refine(mediaPathSchema, (path) => getMediaKind(path) === "image", () => "Must be an image.");
12257
- const videoPathSchema = Type$1.Refine(mediaPathSchema, (path) => getMediaKind(path) === "video", () => "Must be a video.");
12264
+ const imagePathSchema = Type$2.Refine(mediaPathSchema, (path) => getMediaKind(path) === "image", () => "Must be an image.");
12265
+ const videoPathSchema = Type$2.Refine(mediaPathSchema, (path) => getMediaKind(path) === "video", () => "Must be a video.");
12258
12266
  const altSchema = trimmedString(...ALT_CHECKS);
12259
12267
  /** One gallery entry: an image, or a video with an optional poster image. */
12260
- const pageMediaSchema = Type$1.Union([Type$1.Object({
12268
+ const pageMediaSchema = Type$2.Union([Type$2.Object({
12261
12269
  image: imagePathSchema,
12262
12270
  alt: altSchema
12263
- }, { additionalProperties: false }), Type$1.Object({
12271
+ }, { additionalProperties: false }), Type$2.Object({
12264
12272
  video: videoPathSchema,
12265
- poster: Type$1.Optional(imagePathSchema),
12273
+ poster: Type$2.Optional(imagePathSchema),
12266
12274
  alt: altSchema
12267
12275
  }, { additionalProperties: false })]);
12268
12276
  /** The YAML frontmatter of ADDON.md. The title and description come from the manifest, so neither is allowed. */
12269
- const addonPageFrontmatterSchema = Type$1.Object({
12277
+ const addonPageFrontmatterSchema = Type$2.Object({
12270
12278
  banner: imagePathSchema,
12271
- media: Type$1.Array(pageMediaSchema, {
12279
+ media: Type$2.Array(pageMediaSchema, {
12272
12280
  maxItems: 20,
12273
12281
  default: []
12274
12282
  })
12275
12283
  }, { additionalProperties: false });
12276
- const pixelsSchema = Type$1.Integer({ exclusiveMinimum: 0 });
12284
+ const pixelsSchema = Type$2.Integer({ exclusiveMinimum: 0 });
12277
12285
  /** One media file as `ubd build` measured it. */
12278
- const measuredMediaSchema = Type$1.Refine(Type$1.Refine(Type$1.Object({
12286
+ const measuredMediaSchema = Type$2.Refine(Type$2.Refine(Type$2.Object({
12279
12287
  path: mediaPathSchema,
12280
- kind: Type$1.Enum(MEDIA_KINDS),
12288
+ kind: Type$2.Enum(MEDIA_KINDS),
12281
12289
  width: pixelsSchema,
12282
12290
  height: pixelsSchema,
12283
12291
  /** The video's length in seconds. */
12284
- duration: Type$1.Optional(Type$1.Number({ exclusiveMinimum: 0 })),
12292
+ duration: Type$2.Optional(Type$2.Number({ exclusiveMinimum: 0 })),
12285
12293
  /** The base64 ThumbHash placeholder shown while the file loads. */
12286
- thumbhash: Type$1.String({
12294
+ thumbhash: Type$2.String({
12287
12295
  pattern: /^[A-Za-z0-9+/]{1,64}={0,2}$/,
12288
12296
  errorMessage: "Must be base64."
12289
12297
  }),
12290
- bytes: Type$1.Integer({ exclusiveMinimum: 0 })
12298
+ bytes: Type$2.Integer({ exclusiveMinimum: 0 })
12291
12299
  }), (media) => media.kind === getMediaKind(media.path), () => "The kind must match the file extension."), (media) => media.kind === "video" || media.duration === void 0, () => "Only a video has a duration.");
12292
12300
  /** The measured page `ubd build` writes to `{output}/page.json`: the frontmatter and every media file it measured. */
12293
- const addonPageSchema = superRefine(Type$1.Object({
12301
+ const addonPageSchema = superRefine(Type$2.Object({
12294
12302
  frontmatter: addonPageFrontmatterSchema,
12295
- media: Type$1.Array(measuredMediaSchema)
12303
+ media: Type$2.Array(measuredMediaSchema)
12296
12304
  }), measuredPageIssues);
12297
12305
  /**
12298
12306
  * @description Reads ADDON.md's frontmatter as YAML and validates it against the page contract.
@@ -12370,16 +12378,16 @@ const VIDEO_CODECS = {
12370
12378
  ]
12371
12379
  };
12372
12380
  const execFileAsync = promisify(execFile);
12373
- const ffprobeSchema = Type$1.Object({
12374
- streams: Type$1.Array(Type$1.Object({
12375
- codec_name: Type$1.String(),
12376
- width: Type$1.Number(),
12377
- height: Type$1.Number(),
12378
- side_data_list: Type$1.Optional(Type$1.Array(Type$1.Object({ rotation: Type$1.Optional(Type$1.Number()) })))
12381
+ const ffprobeSchema = Type$2.Object({
12382
+ streams: Type$2.Array(Type$2.Object({
12383
+ codec_name: Type$2.String(),
12384
+ width: Type$2.Number(),
12385
+ height: Type$2.Number(),
12386
+ side_data_list: Type$2.Optional(Type$2.Array(Type$2.Object({ rotation: Type$2.Optional(Type$2.Number()) })))
12379
12387
  }), { minItems: 1 }),
12380
- format: Type$1.Object({
12381
- format_name: Type$1.String(),
12382
- duration: Type$1.Optional(Type$1.Unknown())
12388
+ format: Type$2.Object({
12389
+ format_name: Type$2.String(),
12390
+ duration: Type$2.Optional(Type$2.Unknown())
12383
12391
  })
12384
12392
  });
12385
12393
  /**
@@ -12518,27 +12526,27 @@ function describeError(error) {
12518
12526
  }
12519
12527
  // ../contracts/src/api.ts
12520
12528
  /** The origin of the Unbound web API, reduced from any http(s) URL; production when unset. */
12521
- const apiUrlSchema = Type$1.Codec(Type$1.String({
12529
+ const apiUrlSchema = Type$2.Codec(Type$2.String({
12522
12530
  format: "url",
12523
12531
  pattern: /^https?:\/\//i,
12524
12532
  default: "https://unbound.rip"
12525
12533
  })).Decode((url) => new URL(url).origin).Encode((origin) => String(origin));
12526
12534
  // src/lib/env.ts
12527
- const schema$9 = Type$1.Object({
12535
+ const schema$9 = Type$2.Object({
12528
12536
  /** The origin of the Unbound web API that `ubd login` and `ubd publish` talk to. */
12529
12537
  UNBOUND_API_URL: apiUrlSchema,
12530
12538
  /** An API key for CI; when set, `ubd publish` sends it instead of the stored login. */
12531
- UNBOUND_API_KEY: Type$1.Optional(Type$1.String({ minLength: 1 })),
12532
- XDG_CONFIG_HOME: Type$1.Optional(Type$1.String({ minLength: 1 }))
12539
+ UNBOUND_API_KEY: Type$2.Optional(Type$2.String({ minLength: 1 })),
12540
+ XDG_CONFIG_HOME: Type$2.Optional(Type$2.String({ minLength: 1 }))
12533
12541
  });
12534
- const positiveInteger = Type$1.Refine(Type$1.String(), (value) => Number.isInteger(Number(value)) && Number(value) > 0, () => "Too small: expected number to be >0");
12535
- const mediaLimitsSource = Type$1.Object({
12542
+ const positiveInteger = Type$2.Refine(Type$2.String(), (value) => Number.isInteger(Number(value)) && Number(value) > 0, () => "Too small: expected number to be >0");
12543
+ const mediaLimitsSource = Type$2.Object({
12536
12544
  /** The largest media file ADDON.md may reference, in bytes. */
12537
- ADDON_MEDIA_MAX_BYTES: Type$1.Optional(positiveInteger),
12545
+ ADDON_MEDIA_MAX_BYTES: Type$2.Optional(positiveInteger),
12538
12546
  /** The most bytes all of an addon's media may take together. */
12539
- ADDON_MEDIA_TOTAL_MAX_BYTES: Type$1.Optional(positiveInteger)
12547
+ ADDON_MEDIA_TOTAL_MAX_BYTES: Type$2.Optional(positiveInteger)
12540
12548
  });
12541
- const mediaLimitsSchema = Type$1.Decode(mediaLimitsSource, (raw) => ({
12549
+ const mediaLimitsSchema = Type$2.Decode(mediaLimitsSource, (raw) => ({
12542
12550
  ADDON_MEDIA_MAX_BYTES: Number(raw.ADDON_MEDIA_MAX_BYTES ?? 10485760),
12543
12551
  ADDON_MEDIA_TOTAL_MAX_BYTES: Number(raw.ADDON_MEDIA_TOTAL_MAX_BYTES ?? 52428800)
12544
12552
  }));
@@ -12797,15 +12805,15 @@ const IOS_TIMEOUTS = {
12797
12805
  restart: 6e4
12798
12806
  };
12799
12807
  const DEVICE_TIMEOUT_GRACE_MS = 1e4;
12800
- const shellOutputSchema = Type$1.Object({
12801
- exitCode: Type$1.Optional(Type$1.Number()),
12802
- output: Type$1.Optional(Type$1.String())
12808
+ const shellOutputSchema = Type$2.Object({
12809
+ exitCode: Type$2.Optional(Type$2.Number()),
12810
+ output: Type$2.Optional(Type$2.String())
12803
12811
  });
12804
- const deviceListSchema = Type$1.Object({ devices: Type$1.Optional(Type$1.Array(Type$1.Object({
12805
- id: Type$1.Unknown(),
12806
- bridge: Type$1.Optional(Type$1.Boolean())
12812
+ const deviceListSchema = Type$2.Object({ devices: Type$2.Optional(Type$2.Array(Type$2.Object({
12813
+ id: Type$2.Unknown(),
12814
+ bridge: Type$2.Optional(Type$2.Boolean())
12807
12815
  }))) });
12808
- const appContainerSchema = Type$1.Object({ dataContainer: Type$1.Optional(Type$1.String()) });
12816
+ const appContainerSchema = Type$2.Object({ dataContainer: Type$2.Optional(Type$2.String()) });
12809
12817
  /**
12810
12818
  * @description Finds the `jb-p1lot` binary on the PATH, falling back to Go's install directory.
12811
12819
  * @param env The environment to read `PATH` and `GOPATH` from.
@@ -13069,13 +13077,13 @@ const installCommand = {
13069
13077
  cli: "root",
13070
13078
  mcp: true
13071
13079
  },
13072
- schema: Type$1.Object({
13073
- addon: defaulted(Type$1.String({ description: "The plugin to install, as its manifest id or directory." }), "."),
13074
- build: defaulted(Type$1.Boolean({ description: "Build the plugin before installing it." }), true),
13075
- serial: Type$1.Optional(Type$1.String({ description: "The adb serial of the Android device, when several are attached." })),
13076
- ios: defaulted(Type$1.Boolean({ description: "Install on a jailbroken iPhone through jb-p1lot instead of over adb." }), false),
13077
- udid: Type$1.Optional(Type$1.String({ description: "The UDID of the iPhone, when several are connected. Implies --ios." })),
13078
- restart: defaulted(Type$1.Boolean({ description: "Restart Discord afterwards so it loads the plugin." }), false)
13080
+ schema: Type$2.Object({
13081
+ addon: defaulted(Type$2.String({ description: "The plugin to install, as its manifest id or directory." }), "."),
13082
+ build: defaulted(Type$2.Boolean({ description: "Build the plugin before installing it." }), true),
13083
+ serial: Type$2.Optional(Type$2.String({ description: "The adb serial of the Android device, when several are attached." })),
13084
+ ios: defaulted(Type$2.Boolean({ description: "Install on a jailbroken iPhone through jb-p1lot instead of over adb." }), false),
13085
+ udid: Type$2.Optional(Type$2.String({ description: "The UDID of the iPhone, when several are connected. Implies --ios." })),
13086
+ restart: defaulted(Type$2.Boolean({ description: "Restart Discord afterwards so it loads the plugin." }), false)
13079
13087
  }),
13080
13088
  cli: { positionals: ["addon"] },
13081
13089
  async handler({ addon: target, build, serial, ios, udid, restart }) {
@@ -13239,9 +13247,9 @@ function migrateEntry(code, id) {
13239
13247
  };
13240
13248
  }
13241
13249
  // src/commands/migrate.ts
13242
- const schema$8 = Type$1.Object({
13243
- what: Type$1.Enum(["entry"], { description: "What to migrate: entry." }),
13244
- dir: defaulted(Type$1.String({ description: "A plugin folder, or a folder of plugins, whose src/index entries to migrate." }), ".")
13250
+ const schema$8 = Type$2.Object({
13251
+ what: Type$2.Enum(["entry"], { description: "What to migrate: entry." }),
13252
+ dir: defaulted(Type$2.String({ description: "A plugin folder, or a folder of plugins, whose src/index entries to migrate." }), ".")
13245
13253
  });
13246
13254
  function entriesIn(dir) {
13247
13255
  const own = findFile(resolve(dir, "src", "index"));
@@ -13608,9 +13616,9 @@ const modulesCommand = {
13608
13616
  cli: "root",
13609
13617
  mcp: true
13610
13618
  },
13611
- schema: Type$1.Object({
13612
- pattern: Type$1.String({ description: "A case-insensitive regular expression for file paths or export names." }),
13613
- max: defaulted(Type$1.Number({ description: "How many modules to list." }), 40),
13619
+ schema: Type$2.Object({
13620
+ pattern: Type$2.String({ description: "A case-insensitive regular expression for file paths or export names." }),
13621
+ max: defaulted(Type$2.Number({ description: "How many modules to list." }), 40),
13614
13622
  port: bridgePortField
13615
13623
  }),
13616
13624
  cli: { positionals: ["pattern"] },
@@ -13619,16 +13627,16 @@ const modulesCommand = {
13619
13627
  }
13620
13628
  };
13621
13629
  // src/lib/profile-summary.ts
13622
- const frameId = Type$1.Union([Type$1.String(), Type$1.Number()]);
13623
- const stackFrameSchema = Type$1.Object({
13624
- name: Type$1.Optional(Type$1.String()),
13625
- category: Type$1.Optional(Type$1.String()),
13626
- parent: Type$1.Optional(frameId)
13630
+ const frameId = Type$2.Union([Type$2.String(), Type$2.Number()]);
13631
+ const stackFrameSchema = Type$2.Object({
13632
+ name: Type$2.Optional(Type$2.String()),
13633
+ category: Type$2.Optional(Type$2.String()),
13634
+ parent: Type$2.Optional(frameId)
13627
13635
  });
13628
13636
  /** The parts of a Hermes sampled Chrome trace the summary reads. */
13629
- const chromeTraceSchema = Type$1.Object({
13630
- samples: defaulted(Type$1.Array(Type$1.Object({ sf: Type$1.Optional(frameId) })), []),
13631
- stackFrames: defaulted(Type$1.Record(Type$1.String(), stackFrameSchema), {})
13637
+ const chromeTraceSchema = Type$2.Object({
13638
+ samples: defaulted(Type$2.Array(Type$2.Object({ sf: Type$2.Optional(frameId) })), []),
13639
+ stackFrames: defaulted(Type$2.Record(Type$2.String(), stackFrameSchema), {})
13632
13640
  });
13633
13641
  const NATIVE_FRAME_HOPS = 16;
13634
13642
  function javaScriptFrame(frames, id) {
@@ -13723,17 +13731,17 @@ function formatProfile(summary) {
13723
13731
  }
13724
13732
  // src/commands/profile.ts
13725
13733
  const RESULT_GRACE_MS = 1e4;
13726
- const schema$7 = Type$1.Object({
13727
- duration: defaulted(Type$1.Integer({
13734
+ const schema$7 = Type$2.Object({
13735
+ duration: defaulted(Type$2.Integer({
13728
13736
  minimum: 1,
13729
13737
  maximum: 6e4,
13730
13738
  description: "How long to sample the JavaScript thread, in milliseconds."
13731
13739
  }), 5e3),
13732
- top: defaulted(Type$1.Integer({
13740
+ top: defaulted(Type$2.Integer({
13733
13741
  minimum: 1,
13734
13742
  description: "How many functions and sources to list."
13735
13743
  }), 20),
13736
- out: defaulted(Type$1.String({ description: "Also save the raw Chrome trace here, to open in Perfetto or chrome://tracing." }), ""),
13744
+ out: defaulted(Type$2.String({ description: "Also save the raw Chrome trace here, to open in Perfetto or chrome://tracing." }), ""),
13737
13745
  port: bridgePortField
13738
13746
  });
13739
13747
  async function recordProfile(_unbound, duration) {
@@ -13912,16 +13920,16 @@ const CHANGE_TYPES = [
13912
13920
  "fixed",
13913
13921
  "removed"
13914
13922
  ];
13915
- const changeTypeSchema = Type$1.Enum(CHANGE_TYPES);
13923
+ const changeTypeSchema = Type$2.Enum(CHANGE_TYPES);
13916
13924
  /** An unknown type reads as `changed`, so an older client keeps rendering a newer changelog. */
13917
- const changeTypeValueSchema = Type$1.Codec(Type$1.Any()).Decode((value) => {
13925
+ const changeTypeValueSchema = Type$2.Codec(Type$2.Any()).Decode((value) => {
13918
13926
  const parsed = safeParse(changeTypeSchema, value);
13919
13927
  return parsed.success ? parsed.data : "changed";
13920
13928
  }).Encode((value) => value);
13921
13929
  /** One `### Type` block. Items are plain text; a type this client does not know reads as `changed`. */
13922
- const changelogSectionSchema = Type$1.Object({
13930
+ const changelogSectionSchema = Type$2.Object({
13923
13931
  type: changeTypeValueSchema,
13924
- items: Type$1.Array(Type$1.String({
13932
+ items: Type$2.Array(Type$2.String({
13925
13933
  minLength: 1,
13926
13934
  maxLength: 500
13927
13935
  }), {
@@ -13929,9 +13937,9 @@ const changelogSectionSchema = Type$1.Object({
13929
13937
  maxItems: 50
13930
13938
  })
13931
13939
  });
13932
- Type$1.Object({
13933
- date: Type$1.Union([Type$1.String({ format: "date" }), Type$1.Null()]),
13934
- sections: Type$1.Array(changelogSectionSchema, { maxItems: CHANGE_TYPES.length })
13940
+ Type$2.Object({
13941
+ date: Type$2.Union([Type$2.String({ format: "date" }), Type$2.Null()]),
13942
+ sections: Type$2.Array(changelogSectionSchema, { maxItems: CHANGE_TYPES.length })
13935
13943
  });
13936
13944
  const MAX_ITEMS = 50;
13937
13945
  const MAX_ITEM_LENGTH = 500;
@@ -13943,7 +13951,7 @@ const FOLDED_HEADINGS = {
13943
13951
  const RELEASE_HEADING = /^(?:\[([^\]]+)\]|([^\s[\]]+))(?:\s+-\s+(\S+))?$/;
13944
13952
  const LINK_DEFINITION = /^\[[^\]]+\]:\s*\S+/;
13945
13953
  const BULLET = /^[-*](?:\s+(.*))?$/;
13946
- const ISO_DATE = Type$1.String({ format: "date" });
13954
+ const ISO_DATE = Type$2.String({ format: "date" });
13947
13955
  /**
13948
13956
  * @description Parses a `CHANGELOG.md`, collecting every issue instead of stopping at the first. Never throws.
13949
13957
  * @param text The file's text.
@@ -14121,14 +14129,14 @@ function addIssue(parser, line, message) {
14121
14129
  });
14122
14130
  }
14123
14131
  // ../contracts/src/errors.ts
14124
- const apiErrorCodeSchema = Type$1.Enum([...API_ERROR_CODES]);
14125
- Type$1.Object({ error: Type$1.Object({
14132
+ const apiErrorCodeSchema = Type$2.Enum([...API_ERROR_CODES]);
14133
+ Type$2.Object({ error: Type$2.Object({
14126
14134
  code: apiErrorCodeSchema,
14127
- message: Type$1.String()
14135
+ message: Type$2.String()
14128
14136
  }) });
14129
14137
  // ../contracts/src/source.ts
14130
14138
  /** A GitHub repository as `owner/name`. */
14131
- const repositorySchema = Type$1.String({
14139
+ const repositorySchema = Type$2.String({
14132
14140
  pattern: /^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\/[A-Za-z0-9._-]+$/,
14133
14141
  errorMessage: "Must be a GitHub owner/name."
14134
14142
  });
@@ -14166,139 +14174,139 @@ function readRepository$1(input) {
14166
14174
  return parseRepository(input)?.repository ?? input;
14167
14175
  }
14168
14176
  /** Accepts every form {@link parseRepository} reads and outputs `owner/name`. */
14169
- const repositoryInputSchema = Type$1.Codec(Type$1.Refine(Type$1.String({ maxLength: 300 }), (input) => parseRepository(input) !== void 0, () => "Must be a GitHub repository URL or owner/name.")).Decode((input) => readRepository$1(input)).Encode((repository) => repository);
14177
+ const repositoryInputSchema = Type$2.Codec(Type$2.Refine(Type$2.String({ maxLength: 300 }), (input) => parseRepository(input) !== void 0, () => "Must be a GitHub repository URL or owner/name.")).Decode((input) => readRepository$1(input)).Encode((repository) => repository);
14170
14178
  /** A full, lowercase git object id. */
14171
- const commitShaSchema = Type$1.String({
14179
+ const commitShaSchema = Type$2.String({
14172
14180
  pattern: /^[0-9a-f]{40}$/,
14173
14181
  errorMessage: "Must be a full commit SHA."
14174
14182
  });
14175
14183
  /** A branch, tag or full commit SHA. */
14176
- const gitRefSchema = Type$1.String({
14184
+ const gitRefSchema = Type$2.String({
14177
14185
  minLength: 1,
14178
14186
  maxLength: 255,
14179
14187
  pattern: /^[^\s~^:?*[\\]+$/,
14180
14188
  errorMessage: "Must be a branch, tag or commit SHA."
14181
14189
  });
14182
14190
  /** An addon directory relative to the repository root, `/`-separated; `''` is the root. */
14183
- const addonPathSchema = Type$1.Refine(Type$1.String({
14191
+ const addonPathSchema = Type$2.Refine(Type$2.String({
14184
14192
  maxLength: 255,
14185
14193
  pattern: /^(?:[\w.-]+(?:\/[\w.-]+)*)?$/,
14186
14194
  errorMessage: "Must be a directory inside the repository."
14187
14195
  }), (path) => !path.split("/").includes(".."), () => "Must stay inside the repository.");
14188
14196
  /** Where a submitted version was read from: everything a reviewer needs to find the exact bytes. */
14189
- const sourcePinSchema = Type$1.Object({
14197
+ const sourcePinSchema = Type$2.Object({
14190
14198
  repository: repositorySchema,
14191
14199
  /** GitHub's numeric id, which survives renames and transfers. */
14192
- repositoryId: Type$1.Integer({ exclusiveMinimum: 0 }),
14200
+ repositoryId: Type$2.Integer({ exclusiveMinimum: 0 }),
14193
14201
  path: addonPathSchema,
14194
14202
  /** The branch, tag or SHA the author named; `commitSha` is what it resolved to. */
14195
14203
  ref: gitRefSchema,
14196
14204
  commitSha: commitShaSchema,
14197
14205
  /** The shipped file, relative to the repository root. */
14198
- bundlePath: Type$1.String({ minLength: 1 }),
14206
+ bundlePath: Type$2.String({ minLength: 1 }),
14199
14207
  /** The git blob id of `bundlePath` at `commitSha`. */
14200
14208
  bundleBlobSha: commitShaSchema
14201
14209
  });
14202
- const repositoryContributorSchema = Type$1.Object({
14203
- login: Type$1.String({ minLength: 1 }),
14204
- contributions: Type$1.Integer({ minimum: 0 })
14210
+ const repositoryContributorSchema = Type$2.Object({
14211
+ login: Type$2.String({ minLength: 1 }),
14212
+ contributions: Type$2.Integer({ minimum: 0 })
14205
14213
  });
14206
- const repositoryReleaseSchema = Type$1.Object({
14207
- tag: Type$1.String({ minLength: 1 }),
14208
- name: Type$1.Union([Type$1.String(), Type$1.Null()]),
14209
- publishedAt: Type$1.Union([Type$1.String({ format: "date-time" }), Type$1.Null()]),
14210
- url: Type$1.String({ format: "url" })
14214
+ const repositoryReleaseSchema = Type$2.Object({
14215
+ tag: Type$2.String({ minLength: 1 }),
14216
+ name: Type$2.Union([Type$2.String(), Type$2.Null()]),
14217
+ publishedAt: Type$2.Union([Type$2.String({ format: "date-time" }), Type$2.Null()]),
14218
+ url: Type$2.String({ format: "url" })
14211
14219
  });
14212
14220
  /** What GitHub said about the repository when the version was submitted; never refreshed live. */
14213
- const repositoryDetailsSchema = Type$1.Object({
14214
- description: Type$1.Union([Type$1.String(), Type$1.Null()]),
14215
- homepage: Type$1.Union([Type$1.String({ format: "url" }), Type$1.Null()]),
14216
- tags: Type$1.Array(Type$1.String({
14221
+ const repositoryDetailsSchema = Type$2.Object({
14222
+ description: Type$2.Union([Type$2.String(), Type$2.Null()]),
14223
+ homepage: Type$2.Union([Type$2.String({ format: "url" }), Type$2.Null()]),
14224
+ tags: Type$2.Array(Type$2.String({
14217
14225
  minLength: 1,
14218
14226
  maxLength: 50
14219
14227
  }), { maxItems: 20 }),
14220
14228
  /** An SPDX id such as `MIT`, or `null` when GitHub detected none. */
14221
- license: Type$1.Union([Type$1.String(), Type$1.Null()]),
14222
- stars: Type$1.Integer({ minimum: 0 }),
14223
- defaultBranch: Type$1.String({ minLength: 1 }),
14224
- htmlUrl: Type$1.String({ format: "url" }),
14225
- readmeUrl: Type$1.Union([Type$1.String({ format: "url" }), Type$1.Null()]),
14226
- contributors: Type$1.Array(repositoryContributorSchema, { maxItems: 10 }),
14227
- latestRelease: Type$1.Union([repositoryReleaseSchema, Type$1.Null()])
14229
+ license: Type$2.Union([Type$2.String(), Type$2.Null()]),
14230
+ stars: Type$2.Integer({ minimum: 0 }),
14231
+ defaultBranch: Type$2.String({ minLength: 1 }),
14232
+ htmlUrl: Type$2.String({ format: "url" }),
14233
+ readmeUrl: Type$2.Union([Type$2.String({ format: "url" }), Type$2.Null()]),
14234
+ contributors: Type$2.Array(repositoryContributorSchema, { maxItems: 10 }),
14235
+ latestRelease: Type$2.Union([repositoryReleaseSchema, Type$2.Null()])
14228
14236
  });
14229
14237
  /** Why an addon cannot be submitted, with the code the submission itself would fail with. */
14230
- const repositoryAddonIssueSchema = Type$1.Object({
14238
+ const repositoryAddonIssueSchema = Type$2.Object({
14231
14239
  code: apiErrorCodeSchema,
14232
- message: Type$1.String()
14240
+ message: Type$2.String()
14233
14241
  });
14234
14242
  /** One addon a repository registers at a commit, as the repository picker lists it. */
14235
- const repositoryAddonSchema = Type$1.Object({
14236
- id: Type$1.String(),
14243
+ const repositoryAddonSchema = Type$2.Object({
14244
+ id: Type$2.String(),
14237
14245
  /** `null` when the manifest does not pass the manifest contract; `issues` says why. */
14238
- name: Type$1.Union([Type$1.String(), Type$1.Null()]),
14239
- version: Type$1.Union([Type$1.String(), Type$1.Null()]),
14240
- kind: Type$1.Enum(["plugin", "theme"]),
14246
+ name: Type$2.Union([Type$2.String(), Type$2.Null()]),
14247
+ version: Type$2.Union([Type$2.String(), Type$2.Null()]),
14248
+ kind: Type$2.Enum(["plugin", "theme"]),
14241
14249
  path: addonPathSchema,
14242
- static: Type$1.Boolean(),
14250
+ static: Type$2.Boolean(),
14243
14251
  /** The file that ships, relative to the repository root. */
14244
- bundle: Type$1.String(),
14245
- hasAddonMd: Type$1.Boolean(),
14246
- hasChangelog: Type$1.Boolean(),
14252
+ bundle: Type$2.String(),
14253
+ hasAddonMd: Type$2.Boolean(),
14254
+ hasChangelog: Type$2.Boolean(),
14247
14255
  /** Why the addon cannot be submitted at this commit as it is; empty when it can. */
14248
- issues: Type$1.Array(repositoryAddonIssueSchema),
14256
+ issues: Type$2.Array(repositoryAddonIssueSchema),
14249
14257
  /** This version is already published, so there is nothing new to submit. */
14250
- upToDate: Type$1.Boolean()
14258
+ upToDate: Type$2.Boolean()
14251
14259
  });
14252
14260
  /** A directory a workspace registers that is not an addon, and why. */
14253
- const skippedDirectorySchema = Type$1.Object({
14254
- path: Type$1.String(),
14255
- reason: Type$1.String()
14261
+ const skippedDirectorySchema = Type$2.Object({
14262
+ path: Type$2.String(),
14263
+ reason: Type$2.String()
14256
14264
  });
14257
- Type$1.Object({
14265
+ Type$2.Object({
14258
14266
  repository: repositoryInputSchema,
14259
14267
  /** Defaults to the repository's default branch. */
14260
- ref: Type$1.Optional(gitRefSchema)
14268
+ ref: Type$2.Optional(gitRefSchema)
14261
14269
  });
14262
- Type$1.Object({
14270
+ Type$2.Object({
14263
14271
  repository: repositorySchema,
14264
14272
  ref: gitRefSchema,
14265
14273
  commitSha: commitShaSchema,
14266
14274
  details: repositoryDetailsSchema,
14267
- addons: Type$1.Array(repositoryAddonSchema),
14268
- skipped: Type$1.Array(skippedDirectorySchema)
14275
+ addons: Type$2.Array(repositoryAddonSchema),
14276
+ skipped: Type$2.Array(skippedDirectorySchema)
14269
14277
  });
14270
14278
  /** A public repository the caller's linked GitHub account can push to. */
14271
- const pushableRepositorySchema = Type$1.Object({
14279
+ const pushableRepositorySchema = Type$2.Object({
14272
14280
  repository: repositorySchema,
14273
- description: Type$1.Union([Type$1.String(), Type$1.Null()]),
14274
- pushedAt: Type$1.Union([Type$1.String({ format: "date-time" }), Type$1.Null()]),
14281
+ description: Type$2.Union([Type$2.String(), Type$2.Null()]),
14282
+ pushedAt: Type$2.Union([Type$2.String({ format: "date-time" }), Type$2.Null()]),
14275
14283
  /** The owning user's or organization's GitHub avatar. */
14276
- ownerAvatarUrl: Type$1.Union([Type$1.String(), Type$1.Null()])
14284
+ ownerAvatarUrl: Type$2.Union([Type$2.String(), Type$2.Null()])
14277
14285
  });
14278
- Type$1.Object({ repositories: Type$1.Array(pushableRepositorySchema) });
14286
+ Type$2.Object({ repositories: Type$2.Array(pushableRepositorySchema) });
14279
14287
  /** A tag's id: a lowercase slug. */
14280
- const tagIdSchema = Type$1.String({
14288
+ const tagIdSchema = Type$2.String({
14281
14289
  pattern: /^[a-z\d]+(?:-[a-z\d]+)*$/,
14282
14290
  maxLength: 64
14283
14291
  });
14284
- Type$1.Object({
14292
+ Type$2.Object({
14285
14293
  id: tagIdSchema,
14286
- label: Type$1.String(),
14294
+ label: Type$2.String(),
14287
14295
  /** A CSS color for the tag's dot. */
14288
- color: Type$1.String()
14296
+ color: Type$2.String()
14289
14297
  });
14290
14298
  /** The tags an author files a submission under, by id. */
14291
- const submissionTagsSchema = Type$1.Refine(Type$1.Array(tagIdSchema, { maxItems: 5 }), (tags) => new Set(tags).size === tags.length, () => "Tag ids must be unique.");
14299
+ const submissionTagsSchema = Type$2.Refine(Type$2.Array(tagIdSchema, { maxItems: 5 }), (tags) => new Set(tags).size === tags.length, () => "Tag ids must be unique.");
14292
14300
  // ../contracts/src/icons.ts
14293
14301
  /** An icon the website shows for an addon: a Nucleo Micro Bold export name, such as `IconPlug`. */
14294
- const addonIconSchema = Type$1.String({
14302
+ const addonIconSchema = Type$2.String({
14295
14303
  pattern: /^Icon[A-Z][A-Za-z\d]*$/,
14296
14304
  maxLength: 64
14297
14305
  });
14298
14306
  // ../contracts/src/publish.ts
14299
14307
  /** `listing` claims a new addon id; `update` publishes a new version of an addon the caller maintains. */
14300
- const submissionKindSchema = Type$1.Enum(["listing", "update"]);
14301
- const submissionStatusSchema = Type$1.Enum([
14308
+ const submissionKindSchema = Type$2.Enum(["listing", "update"]);
14309
+ const submissionStatusSchema = Type$2.Enum([
14302
14310
  "pending_review",
14303
14311
  "changes_requested",
14304
14312
  "approved",
@@ -14307,53 +14315,53 @@ const submissionStatusSchema = Type$1.Enum([
14307
14315
  "expired",
14308
14316
  "cancelled"
14309
14317
  ]);
14310
- Type$1.Enum(["pending_review", "changes_requested"]);
14311
- const reviewDecisionSchema = Type$1.Enum([
14318
+ Type$2.Enum(["pending_review", "changes_requested"]);
14319
+ const reviewDecisionSchema = Type$2.Enum([
14312
14320
  "approved",
14313
14321
  "rejected",
14314
14322
  "changes_requested"
14315
14323
  ]);
14316
- const submissionReviewSchema = Type$1.Object({
14324
+ const submissionReviewSchema = Type$2.Object({
14317
14325
  decision: reviewDecisionSchema,
14318
14326
  /** The rejection reason or requested changes; optional only on approval. */
14319
- note: Type$1.Union([Type$1.String(), Type$1.Null()]),
14320
- reviewedAt: Type$1.String({ format: "date-time" })
14327
+ note: Type$2.Union([Type$2.String(), Type$2.Null()]),
14328
+ reviewedAt: Type$2.String({ format: "date-time" })
14321
14329
  });
14322
- const submissionSchema = Type$1.Object({
14323
- id: Type$1.String({ minLength: 1 }),
14330
+ const submissionSchema = Type$2.Object({
14331
+ id: Type$2.String({ minLength: 1 }),
14324
14332
  addonId: addonIdSchema,
14325
- version: Type$1.String(),
14333
+ version: Type$2.String(),
14326
14334
  kind: submissionKindSchema,
14327
14335
  status: submissionStatusSchema,
14328
14336
  /** Where the submitted version was read from. */
14329
14337
  source: sourcePinSchema,
14330
14338
  /** The reviewer's decision, once one was made. */
14331
- review: Type$1.Union([submissionReviewSchema, Type$1.Null()])
14339
+ review: Type$2.Union([submissionReviewSchema, Type$2.Null()])
14332
14340
  });
14333
14341
  /** The users an owner makes an addon's maintainers, by user id. */
14334
- const submissionMaintainersSchema = Type$1.Refine(Type$1.Array(Type$1.String({
14342
+ const submissionMaintainersSchema = Type$2.Refine(Type$2.Array(Type$2.String({
14335
14343
  minLength: 1,
14336
14344
  maxLength: 64
14337
14345
  }), { maxItems: 20 }), (ids) => new Set(ids).size === ids.length, () => "User ids must be unique.");
14338
14346
  /** What the author chose for the listing; an omitted field keeps what the addon has. */
14339
- const submissionDetailsSchema = Type$1.Object({
14347
+ const submissionDetailsSchema = Type$2.Object({
14340
14348
  /** Replaces the addon's tags on approval; `[]` clears them. */
14341
- tags: Type$1.Optional(submissionTagsSchema),
14349
+ tags: Type$2.Optional(submissionTagsSchema),
14342
14350
  /** Replaces the addon's maintainers other than its owners on approval; `[]` removes them. Owners only. */
14343
- maintainers: Type$1.Optional(submissionMaintainersSchema),
14351
+ maintainers: Type$2.Optional(submissionMaintainersSchema),
14344
14352
  /** The website icon a new listing starts with; updates change it from the dashboard. */
14345
- icon: Type$1.Optional(addonIconSchema)
14353
+ icon: Type$2.Optional(addonIconSchema)
14346
14354
  });
14347
- Type$1.Object({
14355
+ Type$2.Object({
14348
14356
  repository: repositoryInputSchema,
14349
14357
  path: addonPathSchema,
14350
14358
  /** The branch, tag or SHA the author named; it must still resolve to `commitSha`. */
14351
14359
  ref: gitRefSchema,
14352
14360
  commitSha: commitShaSchema,
14353
- details: Type$1.Optional(submissionDetailsSchema)
14361
+ details: Type$2.Optional(submissionDetailsSchema)
14354
14362
  });
14355
- const createSubmissionResponseSchema = Type$1.Object({ submission: submissionSchema });
14356
- Type$1.Object({
14363
+ const createSubmissionResponseSchema = Type$2.Object({ submission: submissionSchema });
14364
+ Type$2.Object({
14357
14365
  repository: repositoryInputSchema,
14358
14366
  path: addonPathSchema,
14359
14367
  /** The tag or branch holding the build commit; it must still resolve to `commitSha`. */
@@ -14361,31 +14369,31 @@ Type$1.Object({
14361
14369
  commitSha: commitShaSchema
14362
14370
  });
14363
14371
  /** `unchanged` when that version was already published, so a re-run is a no-op. */
14364
- const firstPartyPublishStatusSchema = Type$1.Enum([
14372
+ const firstPartyPublishStatusSchema = Type$2.Enum([
14365
14373
  "published",
14366
14374
  "replaced",
14367
14375
  "unchanged"
14368
14376
  ]);
14369
- Type$1.Object({
14377
+ Type$2.Object({
14370
14378
  status: firstPartyPublishStatusSchema,
14371
14379
  addonId: addonIdSchema,
14372
- version: Type$1.String(),
14380
+ version: Type$2.String(),
14373
14381
  /** The SHA-256 of the live bundle of that version. */
14374
- sha256: Type$1.String()
14382
+ sha256: Type$2.String()
14375
14383
  });
14376
14384
  /** One addon of the first-party repository, delisted or not, at its newest published version. */
14377
- const firstPartyAddonSchema = Type$1.Object({
14385
+ const firstPartyAddonSchema = Type$2.Object({
14378
14386
  id: addonIdSchema,
14379
14387
  /** The newest version ever published, rolled back or not, so it is never published again. */
14380
- version: Type$1.String(),
14388
+ version: Type$2.String(),
14381
14389
  /** The commit that version was read from, or `null` for a legacy upload. */
14382
- commitSha: Type$1.Union([Type$1.String(), Type$1.Null()]),
14390
+ commitSha: Type$2.Union([Type$2.String(), Type$2.Null()]),
14383
14391
  /** The SHA-256 of that version's published bundle. */
14384
- sha256: Type$1.Optional(Type$1.String()),
14392
+ sha256: Type$2.Optional(Type$2.String()),
14385
14393
  /** That version's `manifest.bytecode`, when it ships compiled bundles. */
14386
- bytecode: Type$1.Optional(bytecodeDigestsSchema)
14394
+ bytecode: Type$2.Optional(bytecodeDigestsSchema)
14387
14395
  });
14388
- Type$1.Object({ addons: Type$1.Array(firstPartyAddonSchema) });
14396
+ Type$2.Object({ addons: Type$2.Array(firstPartyAddonSchema) });
14389
14397
  // ../../apps/web/src/api/schema-client.ts
14390
14398
  /**
14391
14399
  * @description Creates a typed client for the Unbound web API that parses each answer with the
@@ -14402,11 +14410,11 @@ function createApiClient(options) {
14402
14410
  };
14403
14411
  }
14404
14412
  // src/lib/credentials.ts
14405
- const schema$6 = Type$1.Object({
14413
+ const schema$6 = Type$2.Object({
14406
14414
  /** The API origin the session belongs to; it is never sent to any other origin. */
14407
- apiUrl: Type$1.String({ format: "url" }),
14415
+ apiUrl: Type$2.String({ format: "url" }),
14408
14416
  /** The Better Auth session token from the device authorization flow, sent as a bearer token. */
14409
- token: Type$1.String({ minLength: 1 })
14417
+ token: Type$2.String({ minLength: 1 })
14410
14418
  });
14411
14419
  /**
14412
14420
  * @description Reads the stored credentials, throwing when the file exists but is not valid.
@@ -14557,10 +14565,10 @@ const publishCommand = {
14557
14565
  cli: "root",
14558
14566
  mcp: false
14559
14567
  },
14560
- schema: Type$1.Object({
14561
- addon: Type$1.Optional(Type$1.String({ description: "The id of the addon to publish; required when the workspace has several." })),
14562
- ref: Type$1.Optional(Type$1.String({ description: "The branch, tag or commit to submit; defaults to the tag on HEAD, else its SHA." })),
14563
- repository: Type$1.Optional(Type$1.String({ description: "The GitHub repository to submit from; defaults to the `origin` remote." }))
14568
+ schema: Type$2.Object({
14569
+ addon: Type$2.Optional(Type$2.String({ description: "The id of the addon to publish; required when the workspace has several." })),
14570
+ ref: Type$2.Optional(Type$2.String({ description: "The branch, tag or commit to submit; defaults to the tag on HEAD, else its SHA." })),
14571
+ repository: Type$2.Optional(Type$2.String({ description: "The GitHub repository to submit from; defaults to the `origin` remote." }))
14564
14572
  }),
14565
14573
  async handler({ addon: id, ref, repository }) {
14566
14574
  try {
@@ -14610,14 +14618,13 @@ function toFailure(error) {
14610
14618
  };
14611
14619
  }
14612
14620
  // src/commands/bridge.ts
14613
- const schema$5 = Type$1.Object({
14614
- action: Type$1.Enum(["restart", "start"], { description: "Restart the running bridge, or start one when none runs." }),
14621
+ const schema$5 = Type$2.Object({
14622
+ action: Type$2.Enum(["restart", "start"], { description: "Restart the running bridge, or start one when none runs." }),
14615
14623
  port: bridgePortField
14616
14624
  });
14617
14625
  const LISTEN_TIMEOUT_MS = 15e3;
14618
14626
  const LISTEN_POLL_MS = 250;
14619
14627
  const LOOKUP_TIMEOUT_MS = 5e3;
14620
- const BRIDGE_ENTRY = join(import.meta.dir, "..", "..", "..", "debugger", "src", "index.ts");
14621
14628
  function isListening(port) {
14622
14629
  return new Promise((resolve) => {
14623
14630
  const socket = connect({
@@ -14660,8 +14667,9 @@ function bridgeLogPath(port) {
14660
14667
  function startDetached(port) {
14661
14668
  const log = bridgeLogPath(port);
14662
14669
  const output = openSync(log, "a");
14670
+ const entry = Bun.resolveSync("@unbound-app/debugger", import.meta.dir);
14663
14671
  spawn("bun", [
14664
- BRIDGE_ENTRY,
14672
+ entry,
14665
14673
  "--port",
14666
14674
  String(port)
14667
14675
  ], {
@@ -14749,6 +14757,11 @@ const KEBAB_CASE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
14749
14757
  */
14750
14758
  async function resolveOptions(input, workspace) {
14751
14759
  intro("ubd create");
14760
+ const unprompted = process.stdin.isTTY ? [] : missingFlags(input, Boolean(workspace));
14761
+ if (unprompted.length) {
14762
+ cancel(`No terminal to prompt in. Pass ${unprompted.join(", ")}.`);
14763
+ process.exit(2);
14764
+ }
14752
14765
  const shape = await resolveShape(input.shape, Boolean(workspace));
14753
14766
  const kind = await resolveKind(input.kind);
14754
14767
  const name = await resolveName(input.name);
@@ -14773,6 +14786,27 @@ async function resolveOptions(input, workspace) {
14773
14786
  install: repository ? await resolveConfirm(input.install, "Install dependencies with bun?") : false
14774
14787
  };
14775
14788
  }
14789
+ /** The flag for each value `ubd create` prompts for, in prompt order; the optional author id has a fallback instead. */
14790
+ const PROMPTED_FLAGS = [
14791
+ ["shape", "--shape"],
14792
+ ["kind", "--kind"],
14793
+ ["name", "--name"],
14794
+ ["displayName", "--display-name"],
14795
+ ["description", "--description"],
14796
+ ["authorName", "--author-name"],
14797
+ ["id", "--id"],
14798
+ ["dir", "--dir"],
14799
+ ["gitInit", "--git-init"],
14800
+ ["install", "--install"]
14801
+ ];
14802
+ function missingFlags(input, hasWorkspace) {
14803
+ const repository = input.shape === "repository";
14804
+ return PROMPTED_FLAGS.filter(([key]) => {
14805
+ if (key === "gitInit" || key === "install") return repository && input[key] === void 0;
14806
+ if (key === "dir" && !repository && hasWorkspace) return false;
14807
+ return input[key] === void 0;
14808
+ }).map(([, flag]) => flag);
14809
+ }
14776
14810
  async function resolveShape(flag, hasWorkspace) {
14777
14811
  if (flag === "repository") return "repository";
14778
14812
  if (flag === "addon") return hasWorkspace ? "add" : "addon";
@@ -15005,18 +15039,18 @@ const createCommand = {
15005
15039
  cli: "root",
15006
15040
  mcp: false
15007
15041
  },
15008
- schema: Type$1.Object({
15009
- kind: Type$1.Optional(Type$1.Enum(["plugin", "theme"], { description: "The addon kind to scaffold." })),
15010
- name: Type$1.Optional(Type$1.String({ description: "The addon name (kebab-case)." })),
15011
- dir: Type$1.Optional(Type$1.String({ description: "The target directory (default ./<name>)." })),
15012
- displayName: Type$1.Optional(Type$1.String({ description: "The human-readable addon name." })),
15013
- description: Type$1.Optional(Type$1.String({ description: "A one-line description." })),
15014
- authorName: Type$1.Optional(Type$1.String({ description: "The author name." })),
15015
- authorId: Type$1.Optional(Type$1.String({ description: "The author Discord id." })),
15016
- id: Type$1.Optional(Type$1.String({ description: "The manifest id (default <authorName>.<name>)." })),
15017
- shape: Type$1.Optional(Type$1.Enum(["repository", "addon"], { description: "Scaffold a repository or a single addon." })),
15018
- gitInit: Type$1.Optional(Type$1.Boolean({ description: "Initialize a git repository (repository shape only)." })),
15019
- install: Type$1.Optional(Type$1.Boolean({ description: "Install dependencies with bun (repository shape only)." }))
15042
+ schema: Type$2.Object({
15043
+ kind: Type$2.Optional(Type$2.Enum(["plugin", "theme"], { description: "The addon kind to scaffold." })),
15044
+ name: Type$2.Optional(Type$2.String({ description: "The addon name (kebab-case)." })),
15045
+ dir: Type$2.Optional(Type$2.String({ description: "The target directory (default ./<name>)." })),
15046
+ displayName: Type$2.Optional(Type$2.String({ description: "The human-readable addon name." })),
15047
+ description: Type$2.Optional(Type$2.String({ description: "A one-line description." })),
15048
+ authorName: Type$2.Optional(Type$2.String({ description: "The author name." })),
15049
+ authorId: Type$2.Optional(Type$2.String({ description: "The author Discord id." })),
15050
+ id: Type$2.Optional(Type$2.String({ description: "The manifest id (default <authorName>.<name>)." })),
15051
+ shape: Type$2.Optional(Type$2.Enum(["repository", "addon"], { description: "Scaffold a repository or a single addon." })),
15052
+ gitInit: Type$2.Optional(Type$2.Boolean({ description: "Initialize a git repository (repository shape only)." })),
15053
+ install: Type$2.Optional(Type$2.Boolean({ description: "Install dependencies with bun (repository shape only)." }))
15020
15054
  }),
15021
15055
  async handler(input) {
15022
15056
  const config = loadConfig();
@@ -15043,9 +15077,12 @@ const createCommand = {
15043
15077
  function printOutro(result) {
15044
15078
  const kindDocs = `${DOCS_BASE_URL}/${result.kind}s/introduction`;
15045
15079
  const manifestDocs = `${DOCS_BASE_URL}/addons/manifest`;
15080
+ const cd = relative(process.cwd(), result.dir) || ".";
15081
+ const installed = !existsSync(join(result.dir, "package.json")) || existsSync(join(result.dir, "node_modules"));
15046
15082
  const steps = [
15047
- `cd ${relative(process.cwd(), result.dir) || "."}`,
15048
- "ubd dev",
15083
+ `cd ${cd}`,
15084
+ ...installed ? [] : ["bun install"],
15085
+ "bunx @unbound-app/cli dev",
15049
15086
  `Learn the ${result.kind} API: ${kindDocs}`,
15050
15087
  `Manifest reference: ${manifestDocs}`
15051
15088
  ];
@@ -15434,18 +15471,18 @@ const deviceCommand = {
15434
15471
  cli: "root",
15435
15472
  mcp: true
15436
15473
  },
15437
- schema: Type$1.Object({
15438
- action: Type$1.Enum([
15474
+ schema: Type$2.Object({
15475
+ action: Type$2.Enum([
15439
15476
  "reset",
15440
15477
  "save",
15441
15478
  "restore",
15442
15479
  "list"
15443
15480
  ], { description: "reset moves Unbound aside for a fresh start, save and restore snapshots, list shows them." }),
15444
- name: defaulted(Type$1.String({ description: "The snapshot to save or restore." }), ""),
15445
- serial: Type$1.Optional(Type$1.String({ description: "The adb serial of the Android device, when several are attached." })),
15446
- ios: defaulted(Type$1.Boolean({ description: "Act on a jailbroken iPhone through jb-p1lot instead of over adb." }), false),
15447
- udid: Type$1.Optional(Type$1.String({ description: "The UDID of the iPhone, when several are connected. Implies --ios." })),
15448
- restart: defaulted(Type$1.Boolean({ description: "Relaunch Discord afterwards." }), false)
15481
+ name: defaulted(Type$2.String({ description: "The snapshot to save or restore." }), ""),
15482
+ serial: Type$2.Optional(Type$2.String({ description: "The adb serial of the Android device, when several are attached." })),
15483
+ ios: defaulted(Type$2.Boolean({ description: "Act on a jailbroken iPhone through jb-p1lot instead of over adb." }), false),
15484
+ udid: Type$2.Optional(Type$2.String({ description: "The UDID of the iPhone, when several are connected. Implies --ios." })),
15485
+ restart: defaulted(Type$2.Boolean({ description: "Relaunch Discord afterwards." }), false)
15449
15486
  }),
15450
15487
  cli: { positionals: ["action", "name"] },
15451
15488
  async handler({ action, name, ...options }) {
@@ -15466,7 +15503,7 @@ const logoutCommand = {
15466
15503
  cli: "root",
15467
15504
  mcp: false
15468
15505
  },
15469
- schema: Type$1.Object({}),
15506
+ schema: Type$2.Object({}),
15470
15507
  async handler() {
15471
15508
  try {
15472
15509
  const path = getCredentialsPath(parseEnv());
@@ -15491,8 +15528,8 @@ const logoutCommand = {
15491
15528
  const TOKEN_KEY = "__UBD_RELOAD__";
15492
15529
  /** How often the reload command probes for the fresh bundle. */
15493
15530
  const RELOAD_POLL_MS = 500;
15494
- const schema$4 = Type$1.Object({
15495
- timeout: defaulted(Type$1.Number({ description: "How long to wait for the reloaded app to reconnect, in milliseconds." }), 6e4),
15531
+ const schema$4 = Type$2.Object({
15532
+ timeout: defaulted(Type$2.Number({ description: "How long to wait for the reloaded app to reconnect, in milliseconds." }), 6e4),
15496
15533
  port: bridgePortField
15497
15534
  });
15498
15535
  function tagAndReload(unbound, key, token) {
@@ -15537,11 +15574,11 @@ const reloadCommand = {
15537
15574
  }
15538
15575
  };
15539
15576
  // src/commands/status.ts
15540
- const schema$3 = Type$1.Object({
15577
+ const schema$3 = Type$2.Object({
15541
15578
  port: bridgePortField,
15542
- watch: defaulted(Type$1.Boolean({ description: "Sample the native resources repeatedly and report which ones keep growing." }), false),
15543
- interval: defaulted(Type$1.Number({ description: "Milliseconds between samples when watching." }), 5e3),
15544
- samples: defaulted(Type$1.Number({ description: "How many samples to take when watching." }), 12)
15579
+ watch: defaulted(Type$2.Boolean({ description: "Sample the native resources repeatedly and report which ones keep growing." }), false),
15580
+ interval: defaulted(Type$2.Number({ description: "Milliseconds between samples when watching." }), 5e3),
15581
+ samples: defaulted(Type$2.Number({ description: "How many samples to take when watching." }), 12)
15545
15582
  });
15546
15583
  const WATCHED_RESOURCES = [
15547
15584
  "handles",
@@ -15579,13 +15616,13 @@ function analyzeSamples(samples) {
15579
15616
  growing
15580
15617
  };
15581
15618
  }
15582
- const deviceStatusReportSchema = Type$1.Object({
15583
- started: Type$1.Number(),
15584
- failed: Type$1.Array(Type$1.String()),
15585
- stopped: Type$1.Array(Type$1.String()),
15586
- resources: Type$1.Union([Type$1.Record(Type$1.String(), Type$1.Number()), Type$1.Null()]),
15587
- slowestStarts: defaulted(Type$1.Array(Type$1.Tuple([Type$1.String(), Type$1.Number()])), []),
15588
- startTotal: defaulted(Type$1.Number(), 0)
15619
+ const deviceStatusReportSchema = Type$2.Object({
15620
+ started: Type$2.Number(),
15621
+ failed: Type$2.Array(Type$2.String()),
15622
+ stopped: Type$2.Array(Type$2.String()),
15623
+ resources: Type$2.Union([Type$2.Record(Type$2.String(), Type$2.Number()), Type$2.Null()]),
15624
+ slowestStarts: defaulted(Type$2.Array(Type$2.Tuple([Type$2.String(), Type$2.Number()])), []),
15625
+ startTotal: defaulted(Type$2.Number(), 0)
15589
15626
  });
15590
15627
  function readDeviceStatus(unbound) {
15591
15628
  const collect = globalThis.gc;
@@ -15701,8 +15738,8 @@ const timingCommand = {
15701
15738
  cli: "root",
15702
15739
  mcp: true
15703
15740
  },
15704
- schema: Type$1.Object({
15705
- rounds: defaulted(Type$1.Number({ description: "How many samples to take." }), 3),
15741
+ schema: Type$2.Object({
15742
+ rounds: defaulted(Type$2.Number({ description: "How many samples to take." }), 3),
15706
15743
  port: bridgePortField
15707
15744
  }),
15708
15745
  handler({ rounds }, { client }) {
@@ -15852,10 +15889,10 @@ const buildCommand = {
15852
15889
  cli: "root",
15853
15890
  mcp: false
15854
15891
  },
15855
- schema: Type$1.Object({
15856
- declaredCapabilities: defaulted(Type$1.Boolean({ description: "Keep the hand-written platforms and Capabilities of plugins that still declare them, until they select platform code with native.definePlatform." }), false),
15857
- strictBudget: defaulted(Type$1.Boolean({ description: "Fail the build when a plugin goes over a size budget, instead of warning." }), false),
15858
- verbose: defaulted(Type$1.Boolean({ description: "Also list the largest source modules of each plugin, and log each metro filter the build rewrote into a built-in filter, with its file and line." }), false)
15892
+ schema: Type$2.Object({
15893
+ declaredCapabilities: defaulted(Type$2.Boolean({ description: "Keep the hand-written platforms and Capabilities of plugins that still declare them, until they select platform code with native.definePlatform." }), false),
15894
+ strictBudget: defaulted(Type$2.Boolean({ description: "Fail the build when a plugin goes over a size budget, instead of warning." }), false),
15895
+ verbose: defaulted(Type$2.Boolean({ description: "Also list the largest source modules of each plugin, and log each metro filter the build rewrote into a built-in filter, with its file and line." }), false)
15859
15896
  }),
15860
15897
  async handler({ declaredCapabilities, strictBudget, verbose }) {
15861
15898
  const resolved = loadConfig();
@@ -16249,9 +16286,10 @@ const typesCheck = {
16249
16286
  * @description Type-checks the addon with its `tsconfig.json` through TypeScript 7's `tsc --noEmit -p`.
16250
16287
  * Slow, so `ubd check` runs it only with `--types`.
16251
16288
  * @param context The addon to check.
16252
- * @returns One diagnostic per type error; none for an addon without a tsconfig.
16289
+ * @returns One diagnostic per type error; none for a theme or an addon without a tsconfig.
16253
16290
  */
16254
16291
  async run({ addon }) {
16292
+ if (addon.kind !== "plugin") return [];
16255
16293
  const configPath = findConfig(addon.dir);
16256
16294
  if (!configPath) return [];
16257
16295
  const { diagnostics, failure } = await runTsc([
@@ -16271,15 +16309,15 @@ const typesCheck = {
16271
16309
  const CONFIG_FILE = ".oxlintrc.json";
16272
16310
  const BINARY = join("node_modules", ".bin", "oxlint");
16273
16311
  const MAX_OUTPUT_BYTES$1 = 67108864;
16274
- const lintOutputSchema = Type$1.Object({ diagnostics: Type$1.Array(Type$1.Object({
16275
- message: Type$1.String(),
16276
- code: Type$1.Optional(Type$1.String()),
16277
- severity: Type$1.String(),
16278
- help: Type$1.Optional(Type$1.String()),
16279
- filename: Type$1.String(),
16280
- labels: Type$1.Array(Type$1.Object({ span: Type$1.Object({
16281
- line: Type$1.Number(),
16282
- column: Type$1.Number()
16312
+ const lintOutputSchema = Type$2.Object({ diagnostics: Type$2.Array(Type$2.Object({
16313
+ message: Type$2.String(),
16314
+ code: Type$2.Optional(Type$2.String()),
16315
+ severity: Type$2.String(),
16316
+ help: Type$2.Optional(Type$2.String()),
16317
+ filename: Type$2.String(),
16318
+ labels: Type$2.Array(Type$2.Object({ span: Type$2.Object({
16319
+ line: Type$2.Number(),
16320
+ column: Type$2.Number()
16283
16321
  }) }))
16284
16322
  })) });
16285
16323
  function findUp(from, name) {
@@ -16355,9 +16393,10 @@ const CHECKS = [
16355
16393
  * @description Runs oxlint over the addon with the nearest `.oxlintrc.json`, the plugin lint
16356
16394
  * `docs/developer/addons/standards.mdx` describes.
16357
16395
  * @param context The addon to check.
16358
- * @returns One diagnostic per lint finding, or a warning when oxlint is not installed.
16396
+ * @returns One diagnostic per lint finding, or a warning when oxlint is not installed; none for a theme.
16359
16397
  */
16360
16398
  async run({ addon }) {
16399
+ if (addon.kind !== "plugin") return [];
16361
16400
  const setup = findLintSetup(addon.dir);
16362
16401
  if (!setup) return [diagnostic({
16363
16402
  file: "",
@@ -16567,16 +16606,16 @@ async function checkAddons(addons, selection, cache) {
16567
16606
  // src/lib/check/shard.ts
16568
16607
  const MAX_OUTPUT_BYTES = 268435456;
16569
16608
  const MAX_SHARDS = 8;
16570
- const shardOutputSchema = Type$1.Object({ diagnostics: Type$1.Array(Type$1.Object({
16571
- dir: Type$1.String(),
16572
- file: Type$1.String(),
16573
- line: Type$1.Number(),
16574
- column: Type$1.Number(),
16575
- path: Type$1.Optional(Type$1.String()),
16576
- severity: Type$1.Union([Type$1.Literal("error"), Type$1.Literal("warning")]),
16577
- message: Type$1.String(),
16578
- fix: Type$1.Optional(Type$1.String()),
16579
- check: Type$1.Optional(Type$1.String())
16609
+ const shardOutputSchema = Type$2.Object({ diagnostics: Type$2.Array(Type$2.Object({
16610
+ dir: Type$2.String(),
16611
+ file: Type$2.String(),
16612
+ line: Type$2.Number(),
16613
+ column: Type$2.Number(),
16614
+ path: Type$2.Optional(Type$2.String()),
16615
+ severity: Type$2.Union([Type$2.Literal("error"), Type$2.Literal("warning")]),
16616
+ message: Type$2.String(),
16617
+ fix: Type$2.Optional(Type$2.String()),
16618
+ check: Type$2.Optional(Type$2.String())
16580
16619
  })) });
16581
16620
  function runShard(entry, args, addons) {
16582
16621
  const plugins = addons.flatMap((addon) => ["--plugin", addon.dir]);
@@ -16629,15 +16668,15 @@ async function checkInShards(addons, args, entry = process.argv[1] ?? "") {
16629
16668
  return addons.flatMap((addon) => byDir.get(addon.dir) ?? []);
16630
16669
  }
16631
16670
  // src/commands/check.ts
16632
- const schema$2 = Type$1.Object({
16633
- plugin: defaulted(Type$1.Array(Type$1.String(), { description: "An addon directory to check, repeatable; defaults to the addon holding the current directory." }), []),
16634
- all: defaulted(Type$1.Boolean({ description: "Check every addon in the workspace." }), false),
16635
- watch: defaulted(Type$1.Boolean({ description: "Re-check an addon on every save, printing only what changed." }), false),
16636
- only: defaulted(Type$1.Array(Type$1.String(), { description: `Run only these checks, comma-separated: ${listCheckIds().join(", ")}.` }), []),
16637
- strict: defaulted(Type$1.Boolean({ description: "Fail on warnings too." }), false),
16638
- types: defaulted(Type$1.Boolean({ description: "Also type-check each addon with its tsconfig, like tsc --noEmit; slow." }), false),
16639
- declaredCapabilities: defaulted(Type$1.Boolean({ description: "Check the capabilities as `ubd build --declared-capabilities` does, keeping hand-written platforms and Capabilities." }), false),
16640
- json: defaulted(Type$1.Boolean({ description: "Print the diagnostics as JSON." }), false)
16671
+ const schema$2 = Type$2.Object({
16672
+ plugin: defaulted(Type$2.Array(Type$2.String(), { description: "An addon directory to check, repeatable; defaults to the addon holding the current directory." }), []),
16673
+ all: defaulted(Type$2.Boolean({ description: "Check every addon in the workspace." }), false),
16674
+ watch: defaulted(Type$2.Boolean({ description: "Re-check an addon on every save, printing only what changed." }), false),
16675
+ only: defaulted(Type$2.Array(Type$2.String(), { description: `Run only these checks, comma-separated: ${listCheckIds().join(", ")}.` }), []),
16676
+ strict: defaulted(Type$2.Boolean({ description: "Fail on warnings too." }), false),
16677
+ types: defaulted(Type$2.Boolean({ description: "Also type-check each addon with its tsconfig, like tsc --noEmit; slow." }), false),
16678
+ declaredCapabilities: defaulted(Type$2.Boolean({ description: "Check the capabilities as `ubd build --declared-capabilities` does, keeping hand-written platforms and Capabilities." }), false),
16679
+ json: defaulted(Type$2.Boolean({ description: "Print the diagnostics as JSON." }), false)
16641
16680
  });
16642
16681
  async function selectAddons(plugins, all) {
16643
16682
  const targets = (plugins.length ? plugins : ["."]).map((plugin) => resolve(plugin));
@@ -16797,7 +16836,7 @@ const loginCommand = {
16797
16836
  cli: "root",
16798
16837
  mcp: false
16799
16838
  },
16800
- schema: Type$1.Object({}),
16839
+ schema: Type$2.Object({}),
16801
16840
  async handler() {
16802
16841
  try {
16803
16842
  const env = parseEnv();
@@ -16881,7 +16920,7 @@ const routeCommand = {
16881
16920
  cli: "mcp",
16882
16921
  mcp: true
16883
16922
  },
16884
- schema: Type$1.Object({ port: bridgePortField }),
16923
+ schema: Type$2.Object({ port: bridgePortField }),
16885
16924
  handler(_input, { client }) {
16886
16925
  return runOnDevice(client, toDeviceCode(currentRoute));
16887
16926
  }
@@ -16894,9 +16933,9 @@ const spansCommand = {
16894
16933
  cli: "root",
16895
16934
  mcp: true
16896
16935
  },
16897
- schema: Type$1.Object({
16898
- match: Type$1.String({ description: "Text the text views must contain." }),
16899
- max: defaulted(Type$1.Number({ description: "How many matching text views to print." }), 5),
16936
+ schema: Type$2.Object({
16937
+ match: Type$2.String({ description: "Text the text views must contain." }),
16938
+ max: defaulted(Type$2.Number({ description: "How many matching text views to print." }), 5),
16900
16939
  port: bridgePortField
16901
16940
  }),
16902
16941
  cli: { positionals: ["match"] },
@@ -16912,13 +16951,13 @@ const traceCommand = {
16912
16951
  cli: "root",
16913
16952
  mcp: true
16914
16953
  },
16915
- schema: Type$1.Object({
16916
- props: Type$1.Array(Type$1.String(), {
16954
+ schema: Type$2.Object({
16955
+ props: Type$2.Array(Type$2.String(), {
16917
16956
  minItems: 1,
16918
16957
  description: "Props of the module to find; the first is the function to trace."
16919
16958
  }),
16920
- duration: defaulted(Type$1.Number({ description: "How long to record calls, in milliseconds." }), 3e3),
16921
- samples: defaulted(Type$1.Number({ description: "How many calls to describe." }), 10),
16959
+ duration: defaulted(Type$2.Number({ description: "How long to record calls, in milliseconds." }), 3e3),
16960
+ samples: defaulted(Type$2.Number({ description: "How many calls to describe." }), 10),
16922
16961
  port: bridgePortField
16923
16962
  }),
16924
16963
  cli: { positionals: ["props"] },
@@ -16934,10 +16973,10 @@ const viewsCommand = {
16934
16973
  cli: "root",
16935
16974
  mcp: true
16936
16975
  },
16937
- schema: Type$1.Object({
16938
- filter: defaulted(Type$1.String({ description: "Keep only views whose class name contains this text, plus their ancestors." }), ""),
16939
- depth: defaulted(Type$1.Number({ description: "How deep to print below each view; iOS caps this at 12 levels." }), 40),
16940
- max: defaulted(Type$1.Number({ description: "How many views to visit at most." }), 1500),
16976
+ schema: Type$2.Object({
16977
+ filter: defaulted(Type$2.String({ description: "Keep only views whose class name contains this text, plus their ancestors." }), ""),
16978
+ depth: defaulted(Type$2.Number({ description: "How deep to print below each view; iOS caps this at 12 levels." }), 40),
16979
+ max: defaulted(Type$2.Number({ description: "How many views to visit at most." }), 1500),
16941
16980
  port: bridgePortField
16942
16981
  }),
16943
16982
  cli: { positionals: ["filter"] },
@@ -16954,10 +16993,10 @@ const evalCommand = {
16954
16993
  cli: "root",
16955
16994
  mcp: true
16956
16995
  },
16957
- schema: Type$1.Object({
16958
- code: Type$1.String({ description: "The JavaScript to evaluate on the device." }),
16996
+ schema: Type$2.Object({
16997
+ code: Type$2.String({ description: "The JavaScript to evaluate on the device." }),
16959
16998
  port: bridgePortField,
16960
- timeout: Type$1.Optional(Type$1.Integer({
16999
+ timeout: Type$2.Optional(Type$2.Integer({
16961
17000
  minimum: 1,
16962
17001
  maximum: MAX_EVAL_TIMEOUT_MS,
16963
17002
  description: `How long to wait for the result, in milliseconds. Defaults to the bridge's ${DEFAULT_EVAL_TIMEOUT_MS}ms.`
@@ -16976,12 +17015,12 @@ const hookCommand = {
16976
17015
  cli: "root",
16977
17016
  mcp: true
16978
17017
  },
16979
- schema: Type$1.Object({
16980
- className: Type$1.String({ description: "The class that declares or inherits the method." }),
16981
- method: Type$1.String({ description: "The Java method name, or the Objective-C selector." }),
16982
- parameters: defaulted(Type$1.Array(Type$1.String(), { description: "The Java parameter type names, such as int or android.view.MotionEvent." }), []),
16983
- duration: defaulted(Type$1.Number({ description: "How long to record calls, in milliseconds." }), 3e3),
16984
- samples: defaulted(Type$1.Number({ description: "How many calls to describe." }), 10),
17018
+ schema: Type$2.Object({
17019
+ className: Type$2.String({ description: "The class that declares or inherits the method." }),
17020
+ method: Type$2.String({ description: "The Java method name, or the Objective-C selector." }),
17021
+ parameters: defaulted(Type$2.Array(Type$2.String(), { description: "The Java parameter type names, such as int or android.view.MotionEvent." }), []),
17022
+ duration: defaulted(Type$2.Number({ description: "How long to record calls, in milliseconds." }), 3e3),
17023
+ samples: defaulted(Type$2.Number({ description: "How many calls to describe." }), 10),
16985
17024
  port: bridgePortField
16986
17025
  }),
16987
17026
  cli: { positionals: ["className", "method"] },
@@ -17185,15 +17224,15 @@ function addSourceKey(pluginRoot, sourceFile, key, text) {
17185
17224
  }
17186
17225
  }
17187
17226
  // src/commands/i18n.ts
17188
- const flag = (description) => defaulted(Type$1.Boolean({ description }), false);
17189
- const schema$1 = Type$1.Object({
17190
- text: defaulted(Type$1.Array(Type$1.String(), { description: "The English text of the key `--add` adds." }), []),
17191
- plugin: Type$1.Optional(Type$1.String({ description: "The plugin directory. Defaults to the current directory." })),
17227
+ const flag = (description) => defaulted(Type$2.Boolean({ description }), false);
17228
+ const schema$1 = Type$2.Object({
17229
+ text: defaulted(Type$2.Array(Type$2.String(), { description: "The English text of the key `--add` adds." }), []),
17230
+ plugin: Type$2.Optional(Type$2.String({ description: "The plugin directory. Defaults to the current directory." })),
17192
17231
  fix: flag("Add each missing key to every translation, with its en-US text as a stub."),
17193
17232
  prune: flag("With --fix, also remove keys en-US does not have. Implies --fix."),
17194
17233
  unused: flag("List en-US keys no source uses, and used keys en-US lacks."),
17195
17234
  strict: flag("Fail on missing and untranslated keys too."),
17196
- add: Type$1.Optional(Type$1.String({ description: "Add this key to en-US with the text given after it." }))
17235
+ add: Type$2.Optional(Type$2.String({ description: "Add this key to en-US with the text given after it." }))
17197
17236
  });
17198
17237
  function findUnused(pluginRoot, source) {
17199
17238
  const { usages, dynamic, failures } = collectPluginKeys(pluginRoot);
@@ -17843,9 +17882,9 @@ const LOG_FILE_POLL_MS = 1e3;
17843
17882
  const LOG_FILE_CHUNK_BYTES = 65536;
17844
17883
  const LOG_FILE_TAIL_LINES = 200;
17845
17884
  const PILOT_TIMEOUT_MS = 15e3;
17846
- const fileReadSchema = Type$1.Object({
17847
- data: Type$1.Optional(Type$1.String()),
17848
- size: Type$1.Number()
17885
+ const fileReadSchema = Type$2.Object({
17886
+ data: Type$2.Optional(Type$2.String()),
17887
+ size: Type$2.Number()
17849
17888
  });
17850
17889
  async function loaderLogPath(target) {
17851
17890
  const container = await discordDataContainer(target, PILOT_TIMEOUT_MS);
@@ -17969,14 +18008,14 @@ async function followLoaderLogFile(options) {
17969
18008
  }
17970
18009
  }
17971
18010
  // src/lib/log-tail.ts
17972
- const archiveEventSchema = Type$1.Object({
17973
- eventType: Type$1.Literal("logEvent"),
17974
- timestamp: Type$1.String(),
17975
- messageType: Type$1.Optional(Type$1.String()),
17976
- processImagePath: Type$1.Optional(Type$1.String()),
17977
- senderImagePath: Type$1.Optional(Type$1.String()),
17978
- processID: Type$1.Optional(Type$1.Number()),
17979
- eventMessage: Type$1.Optional(Type$1.String())
18011
+ const archiveEventSchema = Type$2.Object({
18012
+ eventType: Type$2.Literal("logEvent"),
18013
+ timestamp: Type$2.String(),
18014
+ messageType: Type$2.Optional(Type$2.String()),
18015
+ processImagePath: Type$2.Optional(Type$2.String()),
18016
+ senderImagePath: Type$2.Optional(Type$2.String()),
18017
+ processID: Type$2.Optional(Type$2.Number()),
18018
+ eventMessage: Type$2.Optional(Type$2.String())
17980
18019
  });
17981
18020
  const ADB_TIMEOUT_MS = 15e3;
17982
18021
  const WAIT_FOR_DEVICE_MS = 12e4;
@@ -18333,18 +18372,18 @@ const logsCommand = {
18333
18372
  cli: "root",
18334
18373
  mcp: false
18335
18374
  },
18336
- schema: Type$1.Object({
18337
- serial: Type$1.Optional(Type$1.String({ description: "The adb serial of the Android device, when several are attached." })),
18338
- ios: defaulted(Type$1.Boolean({ description: "Tail a jailbroken iPhone over USB instead of an Android device over adb." }), false),
18339
- udid: Type$1.Optional(Type$1.String({ description: "The UDID of the iPhone, when several are connected. Implies --ios." })),
18340
- level: defaulted(Type$1.String({ description: `The lowest level to print: ${LOG_LEVELS.join(", ")}, or their first letter.` }), "verbose"),
18341
- grep: Type$1.Optional(Type$1.String({ description: "Print only lines matching this regular expression." })),
18342
- since: Type$1.Optional(Type$1.String({ description: "Start from history: a duration such as 5m, a clock time such as 14:30, or a date. iOS history holds notices, errors and faults only." })),
18343
- follow: defaulted(Type$1.Boolean({ description: "Keep printing new lines; pass --follow=false to print the history and exit." }), true),
18344
- symbolicate: defaulted(Type$1.Boolean({ description: "Rewrite Hermes frames of the client bundle to source positions using packages/client/dist's maps." }), false),
18345
- bundle: Type$1.Optional(Type$1.String({ description: "The Hermes bundle to symbolicate against; defaults to the bytecode version the loader reports, else the newest built." })),
18346
- pull: defaulted(Type$1.Boolean({ description: "Copy the loader's own log files off the device instead of tailing; they survive restarts and hold the JavaScript console too." }), false),
18347
- out: Type$1.Optional(Type$1.String({ description: "The folder --pull writes the log files into; defaults to a timestamped folder here." }))
18375
+ schema: Type$2.Object({
18376
+ serial: Type$2.Optional(Type$2.String({ description: "The adb serial of the Android device, when several are attached." })),
18377
+ ios: defaulted(Type$2.Boolean({ description: "Tail a jailbroken iPhone over USB instead of an Android device over adb." }), false),
18378
+ udid: Type$2.Optional(Type$2.String({ description: "The UDID of the iPhone, when several are connected. Implies --ios." })),
18379
+ level: defaulted(Type$2.String({ description: `The lowest level to print: ${LOG_LEVELS.join(", ")}, or their first letter.` }), "verbose"),
18380
+ grep: Type$2.Optional(Type$2.String({ description: "Print only lines matching this regular expression." })),
18381
+ since: Type$2.Optional(Type$2.String({ description: "Start from history: a duration such as 5m, a clock time such as 14:30, or a date. iOS history holds notices, errors and faults only." })),
18382
+ follow: defaulted(Type$2.Boolean({ description: "Keep printing new lines; pass --follow=false to print the history and exit." }), true),
18383
+ symbolicate: defaulted(Type$2.Boolean({ description: "Rewrite Hermes frames of the client bundle to source positions using packages/client/dist's maps." }), false),
18384
+ bundle: Type$2.Optional(Type$2.String({ description: "The Hermes bundle to symbolicate against; defaults to the bytecode version the loader reports, else the newest built." })),
18385
+ pull: defaulted(Type$2.Boolean({ description: "Copy the loader's own log files off the device instead of tailing; they survive restarts and hold the JavaScript console too." }), false),
18386
+ out: Type$2.Optional(Type$2.String({ description: "The folder --pull writes the log files into; defaults to a timestamped folder here." }))
18348
18387
  }),
18349
18388
  async handler({ serial, ios, udid, level, grep, since, follow, symbolicate, bundle, pull, out }) {
18350
18389
  const onIOS = ios || udid !== void 0;
@@ -18415,19 +18454,19 @@ const logsCommand = {
18415
18454
  }
18416
18455
  };
18417
18456
  // src/commands/open.ts
18418
- const schema = Type$1.Object({
18419
- screen: Type$1.String({
18457
+ const schema = Type$2.Object({
18458
+ screen: Type$2.String({
18420
18459
  minLength: 1,
18421
18460
  description: "The settings screen to open: a `Screens` key such as `Marketplace` or `Plugins`, or any `SETTING_RENDERER_CONFIG` route key."
18422
18461
  }),
18423
- params: defaulted(Type$1.String({ description: "Route parameters for the screen, as a JSON object, e.g. `{\"kind\":\"theme\"}`." }), "{}"),
18462
+ params: defaulted(Type$2.String({ description: "Route parameters for the screen, as a JSON object, e.g. `{\"kind\":\"theme\"}`." }), "{}"),
18424
18463
  port: bridgePortField
18425
18464
  });
18426
- const paramsSchema = Type$1.Record(Type$1.String(), Type$1.Union([
18427
- Type$1.String(),
18428
- Type$1.Number(),
18429
- Type$1.Boolean(),
18430
- Type$1.Null()
18465
+ const paramsSchema = Type$2.Record(Type$2.String(), Type$2.Union([
18466
+ Type$2.String(),
18467
+ Type$2.Number(),
18468
+ Type$2.Boolean(),
18469
+ Type$2.Null()
18431
18470
  ]));
18432
18471
  const openCommand = {
18433
18472
  name: "open",
@@ -18453,9 +18492,9 @@ const openCommand = {
18453
18492
  };
18454
18493
  // src/lib/stale-build.ts
18455
18494
  const SKIPPED = /* @__PURE__ */ new Set(["node_modules", ".git"]);
18456
- const packageManifestSchema = Type$1.Object({
18457
- dependencies: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.String(), { additionalProperties: false })),
18458
- devDependencies: Type$1.Optional(Type$1.Record(Type$1.String(), Type$1.String(), { additionalProperties: false }))
18495
+ const packageManifestSchema = Type$2.Object({
18496
+ dependencies: Type$2.Optional(Type$2.Record(Type$2.String(), Type$2.String(), { additionalProperties: false })),
18497
+ devDependencies: Type$2.Optional(Type$2.Record(Type$2.String(), Type$2.String(), { additionalProperties: false }))
18459
18498
  });
18460
18499
  const TEST_FILE = /\.test\.[cm]?[jt]sx?$/;
18461
18500
  function newer(a, b) {
@@ -18545,9 +18584,9 @@ const pushCommand = {
18545
18584
  cli: "root",
18546
18585
  mcp: true
18547
18586
  },
18548
- schema: Type$1.Object({
18549
- addon: defaulted(Type$1.String({ description: "The plugin or theme to push, as its manifest id or directory." }), "."),
18550
- build: defaulted(Type$1.Boolean({ description: "Build the addon before pushing it." }), true),
18587
+ schema: Type$2.Object({
18588
+ addon: defaulted(Type$2.String({ description: "The plugin or theme to push, as its manifest id or directory." }), "."),
18589
+ build: defaulted(Type$2.Boolean({ description: "Build the addon before pushing it." }), true),
18551
18590
  port: bridgePortField
18552
18591
  }),
18553
18592
  cli: { positionals: ["addon"] },
@@ -18707,7 +18746,7 @@ const commands = [
18707
18746
  cli: "root",
18708
18747
  mcp: false
18709
18748
  },
18710
- schema: Type$1.Object({ port: bridgePortField }),
18749
+ schema: Type$2.Object({ port: bridgePortField }),
18711
18750
  async handler(_input, context) {
18712
18751
  const resolved = loadConfig();
18713
18752
  if ("error" in resolved) return {