ocx 1.0.15 → 1.0.17

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.js CHANGED
@@ -4512,6 +4512,77 @@ import { createHash } from "crypto";
4512
4512
  import { existsSync } from "fs";
4513
4513
  import { mkdir, writeFile } from "fs/promises";
4514
4514
  import { dirname, join } from "path";
4515
+ // package.json
4516
+ var package_default = {
4517
+ name: "ocx",
4518
+ version: "1.0.17",
4519
+ description: "OCX CLI - ShadCN-style registry for OpenCode extensions. Install agents, plugins, skills, and MCP servers.",
4520
+ author: "kdcokenny",
4521
+ license: "MIT",
4522
+ type: "module",
4523
+ main: "./dist/index.js",
4524
+ bin: {
4525
+ ocx: "./dist/index.js"
4526
+ },
4527
+ files: [
4528
+ "dist/index.js",
4529
+ "dist/index.js.map"
4530
+ ],
4531
+ publishConfig: {
4532
+ access: "public"
4533
+ },
4534
+ repository: {
4535
+ type: "git",
4536
+ url: "https://github.com/kdcokenny/ocx.git"
4537
+ },
4538
+ homepage: "https://github.com/kdcokenny/ocx#readme",
4539
+ bugs: {
4540
+ url: "https://github.com/kdcokenny/ocx/issues"
4541
+ },
4542
+ keywords: [
4543
+ "opencode",
4544
+ "cli",
4545
+ "extensions",
4546
+ "agents",
4547
+ "plugins",
4548
+ "mcp",
4549
+ "shadcn",
4550
+ "registry"
4551
+ ],
4552
+ engines: {
4553
+ node: ">=18"
4554
+ },
4555
+ scripts: {
4556
+ build: "bun run scripts/build.ts",
4557
+ "build:binary": "bun run scripts/build-binary.ts",
4558
+ check: "bun check:biome && bun check:types",
4559
+ "check:biome": "biome check .",
4560
+ "check:types": "tsc --noEmit",
4561
+ dev: "bun run src/index.ts",
4562
+ prepublishOnly: "bun run build",
4563
+ test: "bun test"
4564
+ },
4565
+ dependencies: {
4566
+ commander: "^14.0.0",
4567
+ diff: "^8.0.0",
4568
+ fuzzysort: "^3.1.0",
4569
+ "jsonc-parser": "3.3.1",
4570
+ kleur: "^4.1.5",
4571
+ ora: "^8.2.0",
4572
+ remeda: "^2.33.0",
4573
+ zod: "^3.24.0"
4574
+ },
4575
+ devDependencies: {
4576
+ "@types/bun": "latest",
4577
+ "@types/diff": "^8.0.0"
4578
+ }
4579
+ };
4580
+
4581
+ // src/constants.ts
4582
+ var OCX_DOMAIN = "ocx.kdco.dev";
4583
+ var GITHUB_REPO = "kdcokenny/ocx";
4584
+ var OCX_SCHEMA_URL = `https://${OCX_DOMAIN}/schema.json`;
4585
+ var CLI_VERSION = package_default.version;
4515
4586
 
4516
4587
  // ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
4517
4588
  var exports_external = {};
@@ -8533,14 +8604,19 @@ var targetPathSchema = exports_external.string().refine((path) => path.startsWit
8533
8604
  }, {
8534
8605
  message: 'Target must be in a valid directory: ".opencode/{agent|skill|plugin|command|tool|philosophy}/..."'
8535
8606
  });
8607
+ var oauthConfigSchema = exports_external.object({
8608
+ clientId: exports_external.string().optional(),
8609
+ scopes: exports_external.array(exports_external.string()).optional(),
8610
+ authUrl: exports_external.string().optional(),
8611
+ tokenUrl: exports_external.string().optional()
8612
+ });
8536
8613
  var mcpServerObjectSchema = exports_external.object({
8537
8614
  type: exports_external.enum(["remote", "local"]),
8538
- url: exports_external.string().url().optional(),
8539
- command: exports_external.array(exports_external.string()).optional(),
8540
- args: exports_external.array(exports_external.string()).optional(),
8615
+ url: exports_external.string().optional(),
8616
+ command: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional(),
8541
8617
  environment: exports_external.record(exports_external.string()).optional(),
8542
8618
  headers: exports_external.record(exports_external.string()).optional(),
8543
- oauth: exports_external.boolean().optional(),
8619
+ oauth: exports_external.union([exports_external.boolean(), oauthConfigSchema]).optional(),
8544
8620
  enabled: exports_external.boolean().default(true)
8545
8621
  }).refine((data) => {
8546
8622
  if (data.type === "remote" && !data.url) {
@@ -8553,7 +8629,7 @@ var mcpServerObjectSchema = exports_external.object({
8553
8629
  }, {
8554
8630
  message: "Remote MCP servers require 'url', local servers require 'command'"
8555
8631
  });
8556
- var mcpServerRefSchema = exports_external.union([exports_external.string().url(), mcpServerObjectSchema]);
8632
+ var mcpServerRefSchema = exports_external.union([exports_external.string(), mcpServerObjectSchema]);
8557
8633
  var componentFileObjectSchema = exports_external.object({
8558
8634
  path: exports_external.string().min(1, "File path cannot be empty"),
8559
8635
  target: targetPathSchema
@@ -8562,24 +8638,81 @@ var componentFileSchema = exports_external.union([
8562
8638
  exports_external.string().min(1, "File path cannot be empty"),
8563
8639
  componentFileObjectSchema
8564
8640
  ]);
8641
+ var providerConfigSchema = exports_external.object({
8642
+ api: exports_external.string().optional(),
8643
+ headers: exports_external.record(exports_external.string()).optional(),
8644
+ env: exports_external.record(exports_external.string()).optional(),
8645
+ enabled: exports_external.boolean().optional()
8646
+ }).passthrough();
8647
+ var lspConfigSchema = exports_external.object({
8648
+ command: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional(),
8649
+ enabled: exports_external.boolean().optional()
8650
+ }).passthrough();
8651
+ var formatterConfigSchema = exports_external.object({
8652
+ command: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional(),
8653
+ glob: exports_external.string().optional()
8654
+ }).passthrough();
8655
+ var commandConfigSchema = exports_external.object({
8656
+ description: exports_external.string().optional(),
8657
+ run: exports_external.string().optional()
8658
+ }).passthrough();
8659
+ var tuiConfigSchema = exports_external.object({
8660
+ disabled: exports_external.boolean().optional()
8661
+ }).passthrough();
8662
+ var serverConfigSchema = exports_external.object({
8663
+ host: exports_external.string().optional(),
8664
+ port: exports_external.number().optional()
8665
+ }).passthrough();
8666
+ var keybindConfigSchema = exports_external.record(exports_external.string());
8667
+ var watcherConfigSchema = exports_external.object({
8668
+ include: exports_external.array(exports_external.string()).optional(),
8669
+ exclude: exports_external.array(exports_external.string()).optional()
8670
+ }).passthrough();
8565
8671
  var agentConfigSchema = exports_external.object({
8672
+ model: exports_external.string().optional(),
8673
+ description: exports_external.string().optional(),
8674
+ steps: exports_external.number().int().positive().optional(),
8675
+ maxSteps: exports_external.number().int().positive().optional(),
8676
+ mode: exports_external.enum(["primary", "subagent", "all"]).optional(),
8566
8677
  tools: exports_external.record(exports_external.boolean()).optional(),
8567
- temperature: exports_external.number().min(0).max(2).optional(),
8678
+ temperature: exports_external.number().optional(),
8679
+ top_p: exports_external.number().optional(),
8568
8680
  prompt: exports_external.string().optional(),
8569
- permission: exports_external.record(exports_external.enum(["allow", "deny"])).optional()
8681
+ permission: exports_external.record(exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))])).optional(),
8682
+ color: exports_external.string().optional(),
8683
+ disable: exports_external.boolean().optional(),
8684
+ options: exports_external.record(exports_external.any()).optional()
8570
8685
  });
8571
8686
  var permissionConfigSchema = exports_external.object({
8572
8687
  bash: exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))]).optional(),
8573
8688
  edit: exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))]).optional(),
8574
8689
  mcp: exports_external.record(exports_external.enum(["ask", "allow", "deny"])).optional()
8575
- });
8690
+ }).catchall(exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))]));
8576
8691
  var opencodeConfigSchema = exports_external.object({
8692
+ $schema: exports_external.string().optional(),
8693
+ theme: exports_external.string().optional(),
8694
+ logLevel: exports_external.string().optional(),
8695
+ username: exports_external.string().optional(),
8696
+ model: exports_external.string().optional(),
8697
+ small_model: exports_external.string().optional(),
8698
+ default_agent: exports_external.string().optional(),
8577
8699
  mcp: exports_external.record(mcpServerRefSchema).optional(),
8578
8700
  plugin: exports_external.array(exports_external.string()).optional(),
8579
8701
  tools: exports_external.record(exports_external.boolean()).optional(),
8580
8702
  agent: exports_external.record(agentConfigSchema).optional(),
8581
8703
  instructions: exports_external.array(exports_external.string()).optional(),
8582
- permission: permissionConfigSchema.optional()
8704
+ permission: permissionConfigSchema.optional(),
8705
+ provider: exports_external.record(providerConfigSchema).optional(),
8706
+ lsp: exports_external.record(lspConfigSchema).optional(),
8707
+ formatter: exports_external.record(formatterConfigSchema).optional(),
8708
+ command: exports_external.record(commandConfigSchema).optional(),
8709
+ tui: tuiConfigSchema.optional(),
8710
+ server: serverConfigSchema.optional(),
8711
+ keybind: keybindConfigSchema.optional(),
8712
+ watcher: watcherConfigSchema.optional(),
8713
+ auto_update: exports_external.boolean().optional(),
8714
+ auto_compact: exports_external.boolean().optional(),
8715
+ share: exports_external.union([exports_external.boolean(), exports_external.string()]).optional()
8583
8716
  });
8584
8717
  var componentManifestSchema = exports_external.object({
8585
8718
  name: openCodeNameSchema,
@@ -8638,6 +8771,12 @@ var registrySchema = exports_external.object({
8638
8771
  message: "Version must be valid semver (e.g., '1.0.0', '2.1.0-beta.1')"
8639
8772
  }),
8640
8773
  author: exports_external.string().min(1, "Author cannot be empty"),
8774
+ opencode: exports_external.string().regex(semverRegex, {
8775
+ message: "OpenCode version must be valid semver"
8776
+ }).optional(),
8777
+ ocx: exports_external.string().regex(semverRegex, {
8778
+ message: "OCX version must be valid semver"
8779
+ }).optional(),
8641
8780
  components: exports_external.array(componentManifestSchema)
8642
8781
  }).refine((data) => {
8643
8782
  const componentNames = new Set(data.components.map((c) => c.name));
@@ -8664,6 +8803,12 @@ var registryIndexSchema = exports_external.object({
8664
8803
  namespace: namespaceSchema,
8665
8804
  version: exports_external.string(),
8666
8805
  author: exports_external.string(),
8806
+ opencode: exports_external.string().regex(semverRegex, {
8807
+ message: "OpenCode version must be valid semver"
8808
+ }).optional(),
8809
+ ocx: exports_external.string().regex(semverRegex, {
8810
+ message: "OCX version must be valid semver"
8811
+ }).optional(),
8667
8812
  components: exports_external.array(exports_external.object({
8668
8813
  name: openCodeNameSchema,
8669
8814
  type: componentTypeSchema,
@@ -10273,7 +10418,8 @@ var registryConfigSchema = exports_external.object({
10273
10418
  var ocxConfigSchema = exports_external.object({
10274
10419
  $schema: exports_external.string().optional(),
10275
10420
  registries: exports_external.record(registryConfigSchema).default({}),
10276
- lockRegistries: exports_external.boolean().default(false)
10421
+ lockRegistries: exports_external.boolean().default(false),
10422
+ skipCompatCheck: exports_external.boolean().default(false)
10277
10423
  });
10278
10424
  var installedComponentSchema = exports_external.object({
10279
10425
  registry: exports_external.string(),
@@ -10348,11 +10494,28 @@ async function readOpencodeJsonConfig(cwd) {
10348
10494
  async function writeOpencodeJsonConfig(path, content) {
10349
10495
  await Bun.write(path, content);
10350
10496
  }
10497
+ function getValueAtPath(content, path) {
10498
+ const parsed = parse2(content, [], { allowTrailingComma: true });
10499
+ let current = parsed;
10500
+ for (const segment of path) {
10501
+ if (current === null || current === undefined)
10502
+ return;
10503
+ if (typeof current !== "object")
10504
+ return;
10505
+ current = current[segment];
10506
+ }
10507
+ return current;
10508
+ }
10351
10509
  function applyValueAtPath(content, path, value) {
10352
10510
  if (value === null || value === undefined) {
10353
10511
  return content;
10354
10512
  }
10355
10513
  if (typeof value === "object" && !Array.isArray(value)) {
10514
+ const existingValue = getValueAtPath(content, path);
10515
+ if (existingValue !== undefined && (existingValue === null || typeof existingValue !== "object")) {
10516
+ const edits2 = modify(content, path, value, JSONC_OPTIONS);
10517
+ return applyEdits(content, edits2);
10518
+ }
10356
10519
  let updatedContent = content;
10357
10520
  for (const [key, val] of Object.entries(value)) {
10358
10521
  updatedContent = applyValueAtPath(updatedContent, [...path, key], val);
@@ -11927,9 +12090,86 @@ function createSpinner(options2) {
11927
12090
  });
11928
12091
  return spinner;
11929
12092
  }
12093
+ // src/utils/version-compat.ts
12094
+ function parseVersion(v) {
12095
+ const [main2 = ""] = v.split("-");
12096
+ const parts = main2.split(".");
12097
+ const major = parseInt(parts[0] ?? "", 10);
12098
+ const minor = parseInt(parts[1] ?? "", 10);
12099
+ const patch = parseInt(parts[2] ?? "", 10);
12100
+ if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) {
12101
+ return null;
12102
+ }
12103
+ return { major, minor, patch };
12104
+ }
12105
+ function compareSemver(a, b) {
12106
+ const vA = parseVersion(a);
12107
+ const vB = parseVersion(b);
12108
+ if (!vA || !vB) {
12109
+ return null;
12110
+ }
12111
+ if (vA.major !== vB.major)
12112
+ return vA.major - vB.major;
12113
+ if (vA.minor !== vB.minor)
12114
+ return vA.minor - vB.minor;
12115
+ return vA.patch - vB.patch;
12116
+ }
12117
+ function checkCompatibility(requiredVersion, installedVersion, type) {
12118
+ if (!requiredVersion) {
12119
+ return { compatible: true };
12120
+ }
12121
+ const cmp = compareSemver(installedVersion, requiredVersion);
12122
+ if (cmp === null) {
12123
+ return { compatible: true };
12124
+ }
12125
+ if (cmp >= 0) {
12126
+ return { compatible: true };
12127
+ }
12128
+ return {
12129
+ compatible: false,
12130
+ required: requiredVersion,
12131
+ installed: installedVersion,
12132
+ type
12133
+ };
12134
+ }
12135
+ function formatCompatWarning(registryName, result) {
12136
+ const typeLabel = result.type === "opencode" ? "OpenCode" : "OCX CLI";
12137
+ const updateCmd = result.type === "opencode" ? "Update OpenCode to the latest version" : "bun update -g ocx";
12138
+ return `
12139
+ ${kleur_default.yellow().bold("\u26A0 WARN")} ${kleur_default.yellow("Version compatibility notice")}
12140
+
12141
+ Registry ${kleur_default.cyan(`"${registryName}"`)} requires ${typeLabel} ${kleur_default.green(result.required)}
12142
+ You are running ${typeLabel} ${kleur_default.red(result.installed)}
12143
+
12144
+ This may work fine, but if you encounter issues,
12145
+ consider updating: ${kleur_default.dim(updateCmd)}
12146
+
12147
+ To silence: ${kleur_default.dim("--skip-compat-check")} or set ${kleur_default.dim('"skipCompatCheck": true')} in ocx.jsonc
12148
+ `;
12149
+ }
12150
+ function collectCompatIssues(options2) {
12151
+ const { registry, ocxVersion, opencodeVersion } = options2;
12152
+ const issues = [];
12153
+ const ocxResult = checkCompatibility(registry.ocx, ocxVersion, "ocx");
12154
+ if (!ocxResult.compatible) {
12155
+ issues.push(ocxResult);
12156
+ }
12157
+ if (opencodeVersion) {
12158
+ const opencodeResult = checkCompatibility(registry.opencode, opencodeVersion, "opencode");
12159
+ if (!opencodeResult.compatible) {
12160
+ issues.push(opencodeResult);
12161
+ }
12162
+ }
12163
+ return issues;
12164
+ }
12165
+ function warnCompatIssues(registryName, issues) {
12166
+ for (const issue of issues) {
12167
+ logger.log(formatCompatWarning(registryName, issue));
12168
+ }
12169
+ }
11930
12170
  // src/commands/add.ts
11931
12171
  function registerAddCommand(program2) {
11932
- program2.command("add").description("Add components to your project").argument("<components...>", "Components to install").option("-y, --yes", "Skip prompts").option("--dry-run", "Show what would be installed without making changes").option("--cwd <path>", "Working directory", process.cwd()).option("-q, --quiet", "Suppress output").option("-v, --verbose", "Verbose output").option("--json", "Output as JSON").action(async (components, options2) => {
12172
+ program2.command("add").description("Add components to your project").argument("<components...>", "Components to install").option("-y, --yes", "Skip prompts").option("--dry-run", "Show what would be installed without making changes").option("--cwd <path>", "Working directory", process.cwd()).option("-q, --quiet", "Suppress output").option("-v, --verbose", "Verbose output").option("--json", "Output as JSON").option("--skip-compat-check", "Skip version compatibility checks").action(async (components, options2) => {
11933
12173
  try {
11934
12174
  await runAdd(components, options2);
11935
12175
  } catch (error) {
@@ -11953,13 +12193,34 @@ async function runAdd(componentNames, options2) {
11953
12193
  spin?.start();
11954
12194
  try {
11955
12195
  const resolved = await resolveDependencies(config.registries, componentNames);
11956
- spin?.succeed(`Resolved ${resolved.components.length} components`);
11957
12196
  if (options2.verbose) {
11958
12197
  logger.info("Install order:");
11959
12198
  for (const name of resolved.installOrder) {
11960
12199
  logger.info(` - ${name}`);
11961
12200
  }
11962
12201
  }
12202
+ const registryIndexes = new Map;
12203
+ const uniqueBaseUrls = new Map;
12204
+ for (const component of resolved.components) {
12205
+ if (!uniqueBaseUrls.has(component.registryName)) {
12206
+ uniqueBaseUrls.set(component.registryName, component.baseUrl);
12207
+ }
12208
+ }
12209
+ for (const [namespace, baseUrl] of uniqueBaseUrls) {
12210
+ const index = await fetchRegistryIndex(baseUrl);
12211
+ registryIndexes.set(namespace, index);
12212
+ }
12213
+ spin?.succeed(`Resolved ${resolved.components.length} components from ${registryIndexes.size} registries`);
12214
+ const skipCompat = options2.skipCompatCheck || config.skipCompatCheck;
12215
+ if (!skipCompat) {
12216
+ for (const [namespace, index] of registryIndexes) {
12217
+ const issues = collectCompatIssues({
12218
+ registry: { opencode: index.opencode, ocx: index.ocx },
12219
+ ocxVersion: CLI_VERSION
12220
+ });
12221
+ warnCompatIssues(namespace, issues);
12222
+ }
12223
+ }
11963
12224
  if (options2.dryRun) {
11964
12225
  logger.info("");
11965
12226
  logger.info("Dry run - no changes made");
@@ -12044,7 +12305,10 @@ async function runAdd(componentNames, options2) {
12044
12305
  logger.info(` \u2713 Wrote ${f}`);
12045
12306
  }
12046
12307
  }
12047
- const index = await fetchRegistryIndex(component.baseUrl);
12308
+ const index = registryIndexes.get(component.registryName);
12309
+ if (!index) {
12310
+ throw new ValidationError(`Registry index not found for "${component.registryName}". ` + `This is an internal error - please report it at https://github.com/${GITHUB_REPO}/issues`);
12311
+ }
12048
12312
  lock.installed[component.qualifiedName] = {
12049
12313
  registry: component.registryName,
12050
12314
  version: index.version,
@@ -12314,6 +12578,8 @@ async function buildRegistry(options2) {
12314
12578
  namespace: registry.namespace,
12315
12579
  version: registry.version,
12316
12580
  author: registry.author,
12581
+ ...registry.opencode && { opencode: registry.opencode },
12582
+ ...registry.ocx && { ocx: registry.ocx },
12317
12583
  components: registry.components.map((c) => ({
12318
12584
  name: c.name,
12319
12585
  type: c.type,
@@ -13277,12 +13543,6 @@ Diff for ${res.name}:`));
13277
13543
  import { existsSync as existsSync2 } from "fs";
13278
13544
  import { cp, mkdir as mkdir3, readdir, readFile, rm, writeFile as writeFile2 } from "fs/promises";
13279
13545
  import { join as join4 } from "path";
13280
-
13281
- // src/constants.ts
13282
- var OCX_DOMAIN = "ocx.kdco.dev";
13283
- var OCX_SCHEMA_URL = `https://${OCX_DOMAIN}/schema.json`;
13284
-
13285
- // src/commands/init.ts
13286
13546
  var TEMPLATE_REPO = "kdcokenny/ocx";
13287
13547
  var TEMPLATE_PATH = "examples/registry-starter";
13288
13548
  function registerInitCommand(program2) {
@@ -13924,7 +14184,7 @@ async function hashBundle2(files) {
13924
14184
  `));
13925
14185
  }
13926
14186
  // src/index.ts
13927
- var version = "1.0.15";
14187
+ var version = "1.0.17";
13928
14188
  async function main2() {
13929
14189
  const program2 = new Command().name("ocx").description("OpenCode Extensions - Install agents, skills, plugins, and commands").version(version);
13930
14190
  registerInitCommand(program2);
@@ -13950,4 +14210,4 @@ export {
13950
14210
  buildRegistry
13951
14211
  };
13952
14212
 
13953
- //# debugId=0C10B0E363D77B8C64756E2164756E21
14213
+ //# debugId=39EA7590799B6FC364756E2164756E21