ocx 1.0.14 → 1.0.16

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
@@ -8492,7 +8492,7 @@ var openCodeNameSchema = exports_external.string().min(1, "Name cannot be empty"
8492
8492
  });
8493
8493
  var namespaceSchema = openCodeNameSchema;
8494
8494
  var qualifiedComponentSchema = exports_external.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*\/[a-z0-9]+(-[a-z0-9]+)*$/, {
8495
- message: 'Must be in format "namespace/component" (e.g., "kdco/librarian"). Both parts must be lowercase alphanumeric with hyphens.'
8495
+ message: 'Must be in format "namespace/component" (e.g., "kdco/researcher"). Both parts must be lowercase alphanumeric with hyphens.'
8496
8496
  });
8497
8497
  function parseQualifiedComponent(ref) {
8498
8498
  if (!ref.includes("/")) {
@@ -8533,14 +8533,19 @@ var targetPathSchema = exports_external.string().refine((path) => path.startsWit
8533
8533
  }, {
8534
8534
  message: 'Target must be in a valid directory: ".opencode/{agent|skill|plugin|command|tool|philosophy}/..."'
8535
8535
  });
8536
+ var oauthConfigSchema = exports_external.object({
8537
+ clientId: exports_external.string().optional(),
8538
+ scopes: exports_external.array(exports_external.string()).optional(),
8539
+ authUrl: exports_external.string().optional(),
8540
+ tokenUrl: exports_external.string().optional()
8541
+ });
8536
8542
  var mcpServerObjectSchema = exports_external.object({
8537
8543
  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(),
8544
+ url: exports_external.string().optional(),
8545
+ command: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional(),
8541
8546
  environment: exports_external.record(exports_external.string()).optional(),
8542
8547
  headers: exports_external.record(exports_external.string()).optional(),
8543
- oauth: exports_external.boolean().optional(),
8548
+ oauth: exports_external.union([exports_external.boolean(), oauthConfigSchema]).optional(),
8544
8549
  enabled: exports_external.boolean().default(true)
8545
8550
  }).refine((data) => {
8546
8551
  if (data.type === "remote" && !data.url) {
@@ -8553,7 +8558,7 @@ var mcpServerObjectSchema = exports_external.object({
8553
8558
  }, {
8554
8559
  message: "Remote MCP servers require 'url', local servers require 'command'"
8555
8560
  });
8556
- var mcpServerRefSchema = exports_external.union([exports_external.string().url(), mcpServerObjectSchema]);
8561
+ var mcpServerRefSchema = exports_external.union([exports_external.string(), mcpServerObjectSchema]);
8557
8562
  var componentFileObjectSchema = exports_external.object({
8558
8563
  path: exports_external.string().min(1, "File path cannot be empty"),
8559
8564
  target: targetPathSchema
@@ -8562,24 +8567,81 @@ var componentFileSchema = exports_external.union([
8562
8567
  exports_external.string().min(1, "File path cannot be empty"),
8563
8568
  componentFileObjectSchema
8564
8569
  ]);
8570
+ var providerConfigSchema = exports_external.object({
8571
+ api: exports_external.string().optional(),
8572
+ headers: exports_external.record(exports_external.string()).optional(),
8573
+ env: exports_external.record(exports_external.string()).optional(),
8574
+ enabled: exports_external.boolean().optional()
8575
+ }).passthrough();
8576
+ var lspConfigSchema = exports_external.object({
8577
+ command: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional(),
8578
+ enabled: exports_external.boolean().optional()
8579
+ }).passthrough();
8580
+ var formatterConfigSchema = exports_external.object({
8581
+ command: exports_external.union([exports_external.string(), exports_external.array(exports_external.string())]).optional(),
8582
+ glob: exports_external.string().optional()
8583
+ }).passthrough();
8584
+ var commandConfigSchema = exports_external.object({
8585
+ description: exports_external.string().optional(),
8586
+ run: exports_external.string().optional()
8587
+ }).passthrough();
8588
+ var tuiConfigSchema = exports_external.object({
8589
+ disabled: exports_external.boolean().optional()
8590
+ }).passthrough();
8591
+ var serverConfigSchema = exports_external.object({
8592
+ host: exports_external.string().optional(),
8593
+ port: exports_external.number().optional()
8594
+ }).passthrough();
8595
+ var keybindConfigSchema = exports_external.record(exports_external.string());
8596
+ var watcherConfigSchema = exports_external.object({
8597
+ include: exports_external.array(exports_external.string()).optional(),
8598
+ exclude: exports_external.array(exports_external.string()).optional()
8599
+ }).passthrough();
8565
8600
  var agentConfigSchema = exports_external.object({
8601
+ model: exports_external.string().optional(),
8602
+ description: exports_external.string().optional(),
8603
+ steps: exports_external.number().int().positive().optional(),
8604
+ maxSteps: exports_external.number().int().positive().optional(),
8605
+ mode: exports_external.enum(["primary", "subagent", "all"]).optional(),
8566
8606
  tools: exports_external.record(exports_external.boolean()).optional(),
8567
- temperature: exports_external.number().min(0).max(2).optional(),
8607
+ temperature: exports_external.number().optional(),
8608
+ top_p: exports_external.number().optional(),
8568
8609
  prompt: exports_external.string().optional(),
8569
- permission: exports_external.record(exports_external.enum(["allow", "deny"])).optional()
8610
+ permission: exports_external.record(exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))])).optional(),
8611
+ color: exports_external.string().optional(),
8612
+ disable: exports_external.boolean().optional(),
8613
+ options: exports_external.record(exports_external.any()).optional()
8570
8614
  });
8571
8615
  var permissionConfigSchema = exports_external.object({
8572
8616
  bash: exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))]).optional(),
8573
8617
  edit: exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))]).optional(),
8574
8618
  mcp: exports_external.record(exports_external.enum(["ask", "allow", "deny"])).optional()
8575
- });
8619
+ }).catchall(exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))]));
8576
8620
  var opencodeConfigSchema = exports_external.object({
8621
+ $schema: exports_external.string().optional(),
8622
+ theme: exports_external.string().optional(),
8623
+ logLevel: exports_external.string().optional(),
8624
+ username: exports_external.string().optional(),
8625
+ model: exports_external.string().optional(),
8626
+ small_model: exports_external.string().optional(),
8627
+ default_agent: exports_external.string().optional(),
8577
8628
  mcp: exports_external.record(mcpServerRefSchema).optional(),
8578
8629
  plugin: exports_external.array(exports_external.string()).optional(),
8579
8630
  tools: exports_external.record(exports_external.boolean()).optional(),
8580
8631
  agent: exports_external.record(agentConfigSchema).optional(),
8581
8632
  instructions: exports_external.array(exports_external.string()).optional(),
8582
- permission: permissionConfigSchema.optional()
8633
+ permission: permissionConfigSchema.optional(),
8634
+ provider: exports_external.record(providerConfigSchema).optional(),
8635
+ lsp: exports_external.record(lspConfigSchema).optional(),
8636
+ formatter: exports_external.record(formatterConfigSchema).optional(),
8637
+ command: exports_external.record(commandConfigSchema).optional(),
8638
+ tui: tuiConfigSchema.optional(),
8639
+ server: serverConfigSchema.optional(),
8640
+ keybind: keybindConfigSchema.optional(),
8641
+ watcher: watcherConfigSchema.optional(),
8642
+ auto_update: exports_external.boolean().optional(),
8643
+ auto_compact: exports_external.boolean().optional(),
8644
+ share: exports_external.union([exports_external.boolean(), exports_external.string()]).optional()
8583
8645
  });
8584
8646
  var componentManifestSchema = exports_external.object({
8585
8647
  name: openCodeNameSchema,
@@ -8734,7 +8796,7 @@ class IntegrityError extends OCXError {
8734
8796
  ` + ` Found: ${found}
8735
8797
 
8736
8798
  ` + `The registry content has changed since this component was locked.
8737
- ` + `This could indicate tampering or an unauthorized update.`;
8799
+ ` + `Use 'ocx update ${component}' to intentionally update this component.`;
8738
8800
  super(message, "INTEGRITY_ERROR", EXIT_CODES.INTEGRITY);
8739
8801
  this.name = "IntegrityError";
8740
8802
  }
@@ -8773,23 +8835,31 @@ async function fetchRegistryIndex(baseUrl) {
8773
8835
  });
8774
8836
  }
8775
8837
  async function fetchComponent(baseUrl, name) {
8838
+ const result = await fetchComponentVersion(baseUrl, name);
8839
+ return result.manifest;
8840
+ }
8841
+ async function fetchComponentVersion(baseUrl, name, version) {
8776
8842
  const url = `${baseUrl.replace(/\/$/, "")}/components/${name}.json`;
8777
- return fetchWithCache(url, (data) => {
8843
+ return fetchWithCache(`${url}#v=${version ?? "latest"}`, (data) => {
8778
8844
  const packumentResult = packumentSchema.safeParse(data);
8779
8845
  if (!packumentResult.success) {
8780
8846
  throw new ValidationError(`Invalid packument format for "${name}": ${packumentResult.error.message}`);
8781
8847
  }
8782
8848
  const packument = packumentResult.data;
8783
- const latestVersion = packument["dist-tags"].latest;
8784
- const manifest = packument.versions[latestVersion];
8849
+ const resolvedVersion = version ?? packument["dist-tags"].latest;
8850
+ const manifest = packument.versions[resolvedVersion];
8785
8851
  if (!manifest) {
8786
- throw new ValidationError(`Component "${name}" has no manifest for latest version ${latestVersion}`);
8852
+ if (version) {
8853
+ const availableVersions = Object.keys(packument.versions).join(", ");
8854
+ throw new ValidationError(`Component "${name}" has no version "${version}". Available: ${availableVersions}`);
8855
+ }
8856
+ throw new ValidationError(`Component "${name}" has no manifest for latest version ${resolvedVersion}`);
8787
8857
  }
8788
8858
  const manifestResult = componentManifestSchema.safeParse(manifest);
8789
8859
  if (!manifestResult.success) {
8790
- throw new ValidationError(`Invalid component manifest for "${name}@${latestVersion}": ${manifestResult.error.message}`);
8860
+ throw new ValidationError(`Invalid component manifest for "${name}@${resolvedVersion}": ${manifestResult.error.message}`);
8791
8861
  }
8792
- return manifestResult.data;
8862
+ return { manifest: manifestResult.data, version: resolvedVersion };
8793
8863
  });
8794
8864
  }
8795
8865
  async function fetchFileContent(baseUrl, componentName, filePath) {
@@ -10272,7 +10342,8 @@ var installedComponentSchema = exports_external.object({
10272
10342
  version: exports_external.string(),
10273
10343
  hash: exports_external.string(),
10274
10344
  files: exports_external.array(exports_external.string()),
10275
- installedAt: exports_external.string()
10345
+ installedAt: exports_external.string(),
10346
+ updatedAt: exports_external.string().optional()
10276
10347
  });
10277
10348
  var ocxLockSchema = exports_external.object({
10278
10349
  lockVersion: exports_external.literal(1),
@@ -10339,11 +10410,28 @@ async function readOpencodeJsonConfig(cwd) {
10339
10410
  async function writeOpencodeJsonConfig(path, content) {
10340
10411
  await Bun.write(path, content);
10341
10412
  }
10413
+ function getValueAtPath(content, path) {
10414
+ const parsed = parse2(content, [], { allowTrailingComma: true });
10415
+ let current = parsed;
10416
+ for (const segment of path) {
10417
+ if (current === null || current === undefined)
10418
+ return;
10419
+ if (typeof current !== "object")
10420
+ return;
10421
+ current = current[segment];
10422
+ }
10423
+ return current;
10424
+ }
10342
10425
  function applyValueAtPath(content, path, value) {
10343
10426
  if (value === null || value === undefined) {
10344
10427
  return content;
10345
10428
  }
10346
10429
  if (typeof value === "object" && !Array.isArray(value)) {
10430
+ const existingValue = getValueAtPath(content, path);
10431
+ if (existingValue !== undefined && (existingValue === null || typeof existingValue !== "object")) {
10432
+ const edits2 = modify(content, path, value, JSONC_OPTIONS);
10433
+ return applyEdits(content, edits2);
10434
+ }
10347
10435
  let updatedContent = content;
10348
10436
  for (const [key, val] of Object.entries(value)) {
10349
10437
  updatedContent = applyValueAtPath(updatedContent, [...path, key], val);
@@ -13635,12 +13723,292 @@ function registerSearchCommand(program2) {
13635
13723
  }
13636
13724
  });
13637
13725
  }
13726
+
13727
+ // src/commands/update.ts
13728
+ import { createHash as createHash2 } from "crypto";
13729
+ import { existsSync as existsSync3 } from "fs";
13730
+ import { mkdir as mkdir4, writeFile as writeFile3 } from "fs/promises";
13731
+ import { dirname as dirname3, join as join5 } from "path";
13732
+ function registerUpdateCommand(program2) {
13733
+ program2.command("update [components...]").description("Update installed components (use @version suffix to pin, e.g., kdco/agents@1.2.0)").option("--all", "Update all installed components").option("--registry <name>", "Update all components from a specific registry").option("--dry-run", "Preview changes without applying").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) => {
13734
+ try {
13735
+ await runUpdate(components, options2);
13736
+ } catch (error) {
13737
+ handleError(error, { json: options2.json });
13738
+ }
13739
+ });
13740
+ }
13741
+ async function runUpdate(componentNames, options2) {
13742
+ const cwd = options2.cwd ?? process.cwd();
13743
+ const lockPath = join5(cwd, "ocx.lock");
13744
+ const config = await readOcxConfig(cwd);
13745
+ if (!config) {
13746
+ throw new ConfigError("No ocx.jsonc found. Run 'ocx init' first.");
13747
+ }
13748
+ const lock = await readOcxLock(cwd);
13749
+ if (!lock || Object.keys(lock.installed).length === 0) {
13750
+ throw new ConfigError("Nothing installed yet. Run 'ocx add <component>' first.");
13751
+ }
13752
+ const hasComponents = componentNames.length > 0;
13753
+ const hasAll = options2.all === true;
13754
+ const hasRegistry = options2.registry !== undefined;
13755
+ if (!hasComponents && !hasAll && !hasRegistry) {
13756
+ throw new ValidationError(`Specify components, use --all, or use --registry <name>.
13757
+
13758
+ ` + `Examples:
13759
+ ` + ` ocx update kdco/agents # Update specific component
13760
+ ` + ` ocx update --all # Update all installed components
13761
+ ` + " ocx update --registry kdco # Update all from a registry");
13762
+ }
13763
+ if (hasAll && hasComponents) {
13764
+ throw new ValidationError(`Cannot specify components with --all.
13765
+ ` + "Use either 'ocx update --all' or 'ocx update <components>'.");
13766
+ }
13767
+ if (hasRegistry && hasComponents) {
13768
+ throw new ValidationError(`Cannot specify components with --registry.
13769
+ ` + "Use either 'ocx update --registry <name>' or 'ocx update <components>'.");
13770
+ }
13771
+ if (hasAll && hasRegistry) {
13772
+ throw new ValidationError(`Cannot use --all with --registry.
13773
+ ` + "Use either 'ocx update --all' or 'ocx update --registry <name>'.");
13774
+ }
13775
+ const parsedComponents = componentNames.map(parseComponentSpec);
13776
+ for (const spec of parsedComponents) {
13777
+ if (spec.version !== undefined && spec.version === "") {
13778
+ throw new ValidationError(`Invalid version specifier in '${spec.component}@'.` + `
13779
+ Version cannot be empty. Use 'kdco/agents@1.2.0' or omit the version for latest.`);
13780
+ }
13781
+ }
13782
+ const componentsToUpdate = resolveComponentsToUpdate(lock, parsedComponents, options2);
13783
+ if (componentsToUpdate.length === 0) {
13784
+ if (hasRegistry) {
13785
+ throw new NotFoundError(`No installed components from registry '${options2.registry}'.`);
13786
+ }
13787
+ throw new NotFoundError("No matching components found to update.");
13788
+ }
13789
+ const spin = options2.quiet ? null : createSpinner({ text: "Checking for updates..." });
13790
+ spin?.start();
13791
+ const results = [];
13792
+ const updates = [];
13793
+ try {
13794
+ for (const spec of componentsToUpdate) {
13795
+ const qualifiedName = spec.component;
13796
+ const lockEntry = lock.installed[qualifiedName];
13797
+ if (!lockEntry) {
13798
+ throw new NotFoundError(`Component '${qualifiedName}' not found in lock file.`);
13799
+ }
13800
+ const { namespace, component: componentName } = parseQualifiedComponent(qualifiedName);
13801
+ const regConfig = config.registries[namespace];
13802
+ if (!regConfig) {
13803
+ throw new ConfigError(`Registry '${namespace}' not configured. Component '${qualifiedName}' cannot be updated.`);
13804
+ }
13805
+ const fetchResult = await fetchComponentVersion(regConfig.url, componentName, spec.version);
13806
+ const manifest = fetchResult.manifest;
13807
+ const version = fetchResult.version;
13808
+ const normalizedManifest = normalizeComponentManifest(manifest);
13809
+ const files = [];
13810
+ for (const file of normalizedManifest.files) {
13811
+ const content2 = await fetchFileContent(regConfig.url, componentName, file.path);
13812
+ files.push({ path: file.path, content: Buffer.from(content2) });
13813
+ }
13814
+ const newHash = await hashBundle2(files);
13815
+ if (newHash === lockEntry.hash) {
13816
+ results.push({
13817
+ qualifiedName,
13818
+ oldVersion: lockEntry.version,
13819
+ newVersion: version,
13820
+ status: "up-to-date"
13821
+ });
13822
+ } else if (options2.dryRun) {
13823
+ results.push({
13824
+ qualifiedName,
13825
+ oldVersion: lockEntry.version,
13826
+ newVersion: version,
13827
+ status: "would-update"
13828
+ });
13829
+ } else {
13830
+ results.push({
13831
+ qualifiedName,
13832
+ oldVersion: lockEntry.version,
13833
+ newVersion: version,
13834
+ status: "updated"
13835
+ });
13836
+ updates.push({
13837
+ qualifiedName,
13838
+ component: normalizedManifest,
13839
+ files,
13840
+ newHash,
13841
+ newVersion: version,
13842
+ baseUrl: regConfig.url
13843
+ });
13844
+ }
13845
+ }
13846
+ spin?.succeed(`Checked ${componentsToUpdate.length} component(s)`);
13847
+ if (options2.dryRun) {
13848
+ outputDryRun(results, options2);
13849
+ return;
13850
+ }
13851
+ if (updates.length === 0) {
13852
+ if (!options2.quiet && !options2.json) {
13853
+ logger.info("");
13854
+ logger.success("All components are up to date.");
13855
+ }
13856
+ if (options2.json) {
13857
+ console.log(JSON.stringify({ success: true, updated: [], upToDate: results }, null, 2));
13858
+ }
13859
+ return;
13860
+ }
13861
+ const installSpin = options2.quiet ? null : createSpinner({ text: "Updating components..." });
13862
+ installSpin?.start();
13863
+ for (const update of updates) {
13864
+ for (const file of update.files) {
13865
+ const fileObj = update.component.files.find((f) => f.path === file.path);
13866
+ if (!fileObj)
13867
+ continue;
13868
+ const targetPath = join5(cwd, fileObj.target);
13869
+ const targetDir = dirname3(targetPath);
13870
+ if (!existsSync3(targetDir)) {
13871
+ await mkdir4(targetDir, { recursive: true });
13872
+ }
13873
+ await writeFile3(targetPath, file.content);
13874
+ if (options2.verbose) {
13875
+ logger.info(` \u2713 Updated ${fileObj.target}`);
13876
+ }
13877
+ }
13878
+ const existingEntry = lock.installed[update.qualifiedName];
13879
+ if (!existingEntry) {
13880
+ throw new NotFoundError(`Component '${update.qualifiedName}' not found in lock file.`);
13881
+ }
13882
+ lock.installed[update.qualifiedName] = {
13883
+ registry: existingEntry.registry,
13884
+ version: update.newVersion,
13885
+ hash: update.newHash,
13886
+ files: existingEntry.files,
13887
+ installedAt: existingEntry.installedAt,
13888
+ updatedAt: new Date().toISOString()
13889
+ };
13890
+ }
13891
+ installSpin?.succeed(`Updated ${updates.length} component(s)`);
13892
+ await writeFile3(lockPath, JSON.stringify(lock, null, 2), "utf-8");
13893
+ if (options2.json) {
13894
+ console.log(JSON.stringify({
13895
+ success: true,
13896
+ updated: results.filter((r2) => r2.status === "updated"),
13897
+ upToDate: results.filter((r2) => r2.status === "up-to-date")
13898
+ }, null, 2));
13899
+ } else if (!options2.quiet) {
13900
+ logger.info("");
13901
+ for (const result of results) {
13902
+ if (result.status === "updated") {
13903
+ logger.info(` \u2713 ${result.qualifiedName} (${result.oldVersion} \u2192 ${result.newVersion})`);
13904
+ } else if (result.status === "up-to-date" && options2.verbose) {
13905
+ logger.info(` \u25CB ${result.qualifiedName} (already up to date)`);
13906
+ }
13907
+ }
13908
+ logger.info("");
13909
+ logger.success(`Done! Updated ${updates.length} component(s).`);
13910
+ }
13911
+ } catch (error) {
13912
+ spin?.fail("Failed to check for updates");
13913
+ throw error;
13914
+ }
13915
+ }
13916
+ function parseComponentSpec(spec) {
13917
+ const atIndex = spec.lastIndexOf("@");
13918
+ if (atIndex <= 0) {
13919
+ return { component: spec };
13920
+ }
13921
+ return {
13922
+ component: spec.slice(0, atIndex),
13923
+ version: spec.slice(atIndex + 1)
13924
+ };
13925
+ }
13926
+ function resolveComponentsToUpdate(lock, parsedComponents, options2) {
13927
+ const installedComponents = Object.keys(lock.installed);
13928
+ if (options2.all) {
13929
+ return installedComponents.map((c) => ({ component: c }));
13930
+ }
13931
+ if (options2.registry) {
13932
+ return installedComponents.filter((name) => {
13933
+ const entry = lock.installed[name];
13934
+ return entry?.registry === options2.registry;
13935
+ }).map((c) => ({ component: c }));
13936
+ }
13937
+ const result = [];
13938
+ for (const spec of parsedComponents) {
13939
+ const name = spec.component;
13940
+ if (!name.includes("/")) {
13941
+ const suggestions = installedComponents.filter((installed) => installed.endsWith(`/${name}`));
13942
+ if (suggestions.length === 1) {
13943
+ throw new ValidationError(`Ambiguous component '${name}'. Did you mean '${suggestions[0]}'?`);
13944
+ }
13945
+ if (suggestions.length > 1) {
13946
+ throw new ValidationError(`Ambiguous component '${name}'. Found in multiple registries:
13947
+ ` + suggestions.map((s) => ` - ${s}`).join(`
13948
+ `) + `
13949
+
13950
+ Please use a fully qualified name (registry/component).`);
13951
+ }
13952
+ throw new ValidationError(`Component '${name}' must include a registry prefix (e.g., 'kdco/${name}').`);
13953
+ }
13954
+ if (!lock.installed[name]) {
13955
+ throw new NotFoundError(`Component '${name}' is not installed.
13956
+ Run 'ocx add ${name}' to install it first.`);
13957
+ }
13958
+ result.push(spec);
13959
+ }
13960
+ return result;
13961
+ }
13962
+ function outputDryRun(results, options2) {
13963
+ const wouldUpdate = results.filter((r2) => r2.status === "would-update");
13964
+ const upToDate = results.filter((r2) => r2.status === "up-to-date");
13965
+ if (options2.json) {
13966
+ console.log(JSON.stringify({ dryRun: true, wouldUpdate, upToDate }, null, 2));
13967
+ return;
13968
+ }
13969
+ if (!options2.quiet) {
13970
+ logger.info("");
13971
+ if (wouldUpdate.length > 0) {
13972
+ logger.info("Would update:");
13973
+ for (const result of wouldUpdate) {
13974
+ logger.info(` ${result.qualifiedName} (${result.oldVersion} \u2192 ${result.newVersion})`);
13975
+ }
13976
+ }
13977
+ if (upToDate.length > 0 && options2.verbose) {
13978
+ logger.info("");
13979
+ logger.info("Already up to date:");
13980
+ for (const result of upToDate) {
13981
+ logger.info(` ${result.qualifiedName}`);
13982
+ }
13983
+ }
13984
+ if (wouldUpdate.length > 0) {
13985
+ logger.info("");
13986
+ logger.info("Run without --dry-run to apply changes.");
13987
+ } else {
13988
+ logger.info("All components are up to date.");
13989
+ }
13990
+ }
13991
+ }
13992
+ async function hashContent2(content2) {
13993
+ return createHash2("sha256").update(content2).digest("hex");
13994
+ }
13995
+ async function hashBundle2(files) {
13996
+ const sorted = [...files].sort((a, b) => a.path.localeCompare(b.path));
13997
+ const manifestParts = [];
13998
+ for (const file of sorted) {
13999
+ const hash = await hashContent2(file.content);
14000
+ manifestParts.push(`${file.path}:${hash}`);
14001
+ }
14002
+ return hashContent2(manifestParts.join(`
14003
+ `));
14004
+ }
13638
14005
  // src/index.ts
13639
- var version = "1.0.14";
14006
+ var version = "1.0.16";
13640
14007
  async function main2() {
13641
14008
  const program2 = new Command().name("ocx").description("OpenCode Extensions - Install agents, skills, plugins, and commands").version(version);
13642
14009
  registerInitCommand(program2);
13643
14010
  registerAddCommand(program2);
14011
+ registerUpdateCommand(program2);
13644
14012
  registerDiffCommand(program2);
13645
14013
  registerSearchCommand(program2);
13646
14014
  registerRegistryCommand(program2);
@@ -13661,4 +14029,4 @@ export {
13661
14029
  buildRegistry
13662
14030
  };
13663
14031
 
13664
- //# debugId=8679B4448163A59C64756E2164756E21
14032
+ //# debugId=2706150244B1090964756E2164756E21