ocx 1.4.3 → 1.4.4

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
@@ -9924,8 +9924,9 @@ class ProfileNotFoundError extends OCXError {
9924
9924
 
9925
9925
  class ProfileExistsError extends OCXError {
9926
9926
  profile;
9927
- constructor(profile) {
9928
- super(`Profile "${profile}" already exists. Use --force to overwrite.`, "CONFLICT", EXIT_CODES.CONFLICT);
9927
+ constructor(profile, hint) {
9928
+ const message = hint ? `Profile "${profile}" already exists. ${hint}` : `Profile "${profile}" already exists.`;
9929
+ super(message, "CONFLICT", EXIT_CODES.CONFLICT);
9929
9930
  this.profile = profile;
9930
9931
  this.name = "ProfileExistsError";
9931
9932
  }
@@ -10366,8 +10367,16 @@ var installedComponentSchema = exports_external.object({
10366
10367
  installedAt: exports_external.string(),
10367
10368
  updatedAt: exports_external.string().optional()
10368
10369
  });
10370
+ var installedFromSchema = exports_external.object({
10371
+ registry: exports_external.string(),
10372
+ component: exports_external.string(),
10373
+ version: exports_external.string().optional(),
10374
+ hash: exports_external.string(),
10375
+ installedAt: exports_external.string()
10376
+ });
10369
10377
  var ocxLockSchema = exports_external.object({
10370
10378
  lockVersion: exports_external.literal(1),
10379
+ installedFrom: installedFromSchema.optional(),
10371
10380
  installed: exports_external.record(qualifiedComponentSchema, installedComponentSchema).default({})
10372
10381
  });
10373
10382
  var CONFIG_FILE = "ocx.jsonc";
@@ -11116,7 +11125,7 @@ class ConfigResolver {
11116
11125
  // package.json
11117
11126
  var package_default = {
11118
11127
  name: "ocx",
11119
- version: "1.4.3",
11128
+ version: "1.4.4",
11120
11129
  description: "OCX CLI - ShadCN-style registry for OpenCode extensions. Install agents, plugins, skills, and MCP servers.",
11121
11130
  author: "kdcokenny",
11122
11131
  license: "MIT",
@@ -15805,7 +15814,7 @@ async function copyDir(src, dest) {
15805
15814
  }
15806
15815
  function getReleaseTag() {
15807
15816
  if (false) {}
15808
- return `v${"1.4.3"}`;
15817
+ return `v${"1.4.4"}`;
15809
15818
  }
15810
15819
  function getTemplateUrl(version) {
15811
15820
  const ref = version === "main" ? "heads/main" : `tags/${version}`;
@@ -16048,7 +16057,7 @@ function hashBundle2(files) {
16048
16057
  `));
16049
16058
  }
16050
16059
  async function installProfileFromRegistry(options2) {
16051
- const { namespace, component, profileName, force, registryUrl, registries, quiet } = options2;
16060
+ const { namespace, component, profileName, registryUrl, registries, quiet } = options2;
16052
16061
  const parseResult = profileNameSchema.safeParse(profileName);
16053
16062
  if (!parseResult.success) {
16054
16063
  throw new ValidationError(`Invalid profile name: "${profileName}". ` + `Profile names must start with a letter and contain only alphanumeric characters, dots, underscores, or hyphens.`);
@@ -16056,9 +16065,8 @@ async function installProfileFromRegistry(options2) {
16056
16065
  const profileDir = getProfileDir(profileName);
16057
16066
  const qualifiedName = `${namespace}/${component}`;
16058
16067
  const profileExists = existsSync9(profileDir);
16059
- if (profileExists && !force) {
16060
- throw new ConflictError(`Profile "${profileName}" already exists.
16061
- Use --force to overwrite.`);
16068
+ if (profileExists) {
16069
+ throw new ConflictError(`Profile "${profileName}" already exists. Remove it first with 'ocx profile rm ${profileName}'.`);
16062
16070
  }
16063
16071
  const fetchSpin = quiet ? null : createSpinner({ text: `Fetching ${qualifiedName}...` });
16064
16072
  fetchSpin?.start();
@@ -16100,46 +16108,10 @@ Use --force to overwrite.`);
16100
16108
  }
16101
16109
  }
16102
16110
  filesSpin?.succeed(`Downloaded ${normalized.files.length} files`);
16103
- let resolvedDeps = null;
16104
- const dependencyBundles = [];
16105
- if (manifest.dependencies.length > 0) {
16106
- const depsSpin = quiet ? null : createSpinner({ text: "Resolving dependencies..." });
16107
- depsSpin?.start();
16108
- try {
16109
- const depRefs = manifest.dependencies.map((dep) => dep.includes("/") ? dep : `${namespace}/${dep}`);
16110
- resolvedDeps = await resolveDependencies(registries, depRefs);
16111
- for (const depComponent of resolvedDeps.components) {
16112
- const files = [];
16113
- for (const file of depComponent.files) {
16114
- const content2 = await fetchFileContent(depComponent.baseUrl, depComponent.name, file.path);
16115
- const resolvedTarget = resolveTargetPath(file.target, true);
16116
- files.push({
16117
- path: file.path,
16118
- target: resolvedTarget,
16119
- content: Buffer.from(content2)
16120
- });
16121
- }
16122
- const registryIndex = await fetchRegistryIndex(depComponent.baseUrl);
16123
- dependencyBundles.push({
16124
- qualifiedName: depComponent.qualifiedName,
16125
- registryName: depComponent.registryName,
16126
- files,
16127
- hash: hashBundle2(files),
16128
- version: registryIndex.version
16129
- });
16130
- }
16131
- depsSpin?.succeed(`Resolved ${resolvedDeps.components.length} dependencies`);
16132
- } catch (error) {
16133
- depsSpin?.fail("Failed to resolve dependencies");
16134
- throw error;
16135
- }
16136
- }
16137
16111
  const profilesDir = getProfilesDir();
16138
16112
  await mkdir8(profilesDir, { recursive: true, mode: 448 });
16139
16113
  const stagingDir = await mkdtemp(join8(profilesDir, ".staging-"));
16140
- const stagingOpencodeDir = join8(stagingDir, ".opencode");
16141
16114
  try {
16142
- await mkdir8(stagingOpencodeDir, { recursive: true, mode: 448 });
16143
16115
  const writeSpin = quiet ? null : createSpinner({ text: "Writing profile files..." });
16144
16116
  writeSpin?.start();
16145
16117
  for (const file of profileFiles) {
@@ -16152,7 +16124,7 @@ Use --force to overwrite.`);
16152
16124
  }
16153
16125
  for (const file of embeddedFiles) {
16154
16126
  const target = file.target.startsWith(".opencode/") ? file.target.slice(".opencode/".length) : file.target;
16155
- const targetPath = join8(stagingOpencodeDir, target);
16127
+ const targetPath = join8(stagingDir, target);
16156
16128
  const targetDir = dirname4(targetPath);
16157
16129
  if (!existsSync9(targetDir)) {
16158
16130
  await mkdir8(targetDir, { recursive: true });
@@ -16160,27 +16132,10 @@ Use --force to overwrite.`);
16160
16132
  await writeFile3(targetPath, file.content);
16161
16133
  }
16162
16134
  writeSpin?.succeed(`Wrote ${profileFiles.length + embeddedFiles.length} profile files`);
16163
- if (dependencyBundles.length > 0) {
16164
- const depWriteSpin = quiet ? null : createSpinner({ text: "Writing dependency files..." });
16165
- depWriteSpin?.start();
16166
- let depFileCount = 0;
16167
- for (const bundle of dependencyBundles) {
16168
- for (const file of bundle.files) {
16169
- const targetPath = join8(stagingOpencodeDir, file.target);
16170
- const targetDir = dirname4(targetPath);
16171
- if (!existsSync9(targetDir)) {
16172
- await mkdir8(targetDir, { recursive: true });
16173
- }
16174
- await writeFile3(targetPath, file.content);
16175
- depFileCount++;
16176
- }
16177
- }
16178
- depWriteSpin?.succeed(`Wrote ${depFileCount} dependency files`);
16179
- }
16180
16135
  const profileHash = hashBundle2(profileFiles.map((f) => ({ path: f.path, content: f.content })));
16181
16136
  const registryIndex = await fetchRegistryIndex(registryUrl);
16182
16137
  const lock = {
16183
- version: 1,
16138
+ lockVersion: 1,
16184
16139
  installedFrom: {
16185
16140
  registry: namespace,
16186
16141
  component,
@@ -16190,36 +16145,32 @@ Use --force to overwrite.`);
16190
16145
  },
16191
16146
  installed: {}
16192
16147
  };
16193
- for (const bundle of dependencyBundles) {
16194
- lock.installed[bundle.qualifiedName] = {
16195
- registry: bundle.registryName,
16196
- version: bundle.version,
16197
- hash: bundle.hash,
16198
- files: bundle.files.map((f) => f.target),
16199
- installedAt: new Date().toISOString()
16200
- };
16201
- }
16202
- await writeFile3(join8(stagingDir, "ocx.lock"), JSON.stringify(lock, null, "\t"));
16203
- const moveSpin = quiet ? null : createSpinner({ text: "Finalizing installation..." });
16204
- moveSpin?.start();
16148
+ await writeOcxLock(stagingDir, lock, join8(stagingDir, "ocx.lock"));
16149
+ const renameSpin = quiet ? null : createSpinner({ text: "Moving to profile directory..." });
16150
+ renameSpin?.start();
16205
16151
  const profilesDir2 = dirname4(profileDir);
16206
16152
  if (!existsSync9(profilesDir2)) {
16207
16153
  await mkdir8(profilesDir2, { recursive: true, mode: 448 });
16208
16154
  }
16209
- if (profileExists && force) {
16210
- const backupDir = `${profileDir}.backup-${Date.now()}`;
16211
- await rename3(profileDir, backupDir);
16155
+ await rename3(stagingDir, profileDir);
16156
+ renameSpin?.succeed("Profile installed");
16157
+ if (manifest.dependencies.length > 0) {
16158
+ const depsSpin = quiet ? null : createSpinner({ text: "Installing dependencies..." });
16159
+ depsSpin?.start();
16212
16160
  try {
16213
- await rename3(stagingDir, profileDir);
16214
- } catch (err) {
16215
- await rename3(backupDir, profileDir);
16216
- throw err;
16161
+ const depRefs = manifest.dependencies.map((dep) => dep.includes("/") ? dep : `${namespace}/${dep}`);
16162
+ const provider = {
16163
+ cwd: profileDir,
16164
+ getRegistries: () => registries,
16165
+ getComponentPath: () => ""
16166
+ };
16167
+ await runAddCore(depRefs, { profile: profileName }, provider);
16168
+ depsSpin?.succeed(`Installed ${manifest.dependencies.length} dependencies`);
16169
+ } catch (error) {
16170
+ depsSpin?.fail("Failed to install dependencies");
16171
+ throw error;
16217
16172
  }
16218
- await rm3(backupDir, { recursive: true, force: true });
16219
- } else {
16220
- await rename3(stagingDir, profileDir);
16221
16173
  }
16222
- moveSpin?.succeed("Installation complete");
16223
16174
  if (!quiet) {
16224
16175
  logger.info("");
16225
16176
  logger.success(`Installed profile "${profileName}" from ${qualifiedName}`);
@@ -16228,12 +16179,9 @@ Use --force to overwrite.`);
16228
16179
  for (const file of profileFiles) {
16229
16180
  logger.info(` ${file.target}`);
16230
16181
  }
16231
- if (dependencyBundles.length > 0) {
16232
- logger.info("");
16233
- logger.info("Dependencies:");
16234
- for (const bundle of dependencyBundles) {
16235
- logger.info(` ${bundle.qualifiedName}`);
16236
- }
16182
+ for (const file of embeddedFiles) {
16183
+ const target = file.target.startsWith(".opencode/") ? file.target.slice(".opencode/".length) : file.target;
16184
+ logger.info(` ${target}`);
16237
16185
  }
16238
16186
  }
16239
16187
  } catch (error) {
@@ -16252,9 +16200,6 @@ function parseFromOption(from) {
16252
16200
  throw new ValidationError("--from value cannot be empty");
16253
16201
  }
16254
16202
  const trimmed = from.trim();
16255
- if (trimmed.startsWith("./") || trimmed.startsWith("~/") || trimmed.startsWith("/")) {
16256
- return { type: "local-path", path: trimmed };
16257
- }
16258
16203
  const slashCount = (trimmed.match(/\//g) || []).length;
16259
16204
  if (slashCount === 1) {
16260
16205
  const [namespace, component] = trimmed.split("/").map((s) => s.trim());
@@ -16302,12 +16247,11 @@ async function requireGlobalRegistry(namespace) {
16302
16247
  return { config: globalConfig, registryUrl: registry.url };
16303
16248
  }
16304
16249
  function registerProfileAddCommand(parent) {
16305
- parent.command("add <name>").description("Create a new profile, clone from existing, or install from registry").option("--from <source>", "Clone from existing profile or install from registry (e.g., kdco/minimal)").option("-f, --force", "Overwrite existing profile").addHelpText("after", `
16250
+ parent.command("add <name>").description("Create a new profile, clone from existing, or install from registry").option("--from <source>", "Clone from existing profile or install from registry (e.g., kdco/minimal)").addHelpText("after", `
16306
16251
  Examples:
16307
16252
  $ ocx profile add work # Create empty profile
16308
16253
  $ ocx profile add work --from dev # Clone from existing profile
16309
16254
  $ ocx profile add work --from kdco/minimal # Install from registry
16310
- $ ocx profile add work --from kdco/minimal --force # Overwrite existing
16311
16255
  `).action(async (name, options2) => {
16312
16256
  try {
16313
16257
  await runProfileAdd(name, options2);
@@ -16318,28 +16262,15 @@ Examples:
16318
16262
  }
16319
16263
  async function runProfileAdd(name, options2) {
16320
16264
  const manager = await ProfileManager.requireInitialized();
16321
- const profileExists = await manager.exists(name);
16322
- if (profileExists && !options2.force) {
16323
- logger.error(`\u2717 Profile "${name}" already exists`);
16324
- logger.error("");
16325
- logger.error("Use --force to overwrite the existing profile.");
16326
- throw new ProfileExistsError(name);
16327
- }
16328
16265
  if (!options2.from) {
16329
- await createEmptyProfile(manager, name, profileExists);
16266
+ await createEmptyProfile(manager, name);
16330
16267
  return;
16331
16268
  }
16332
16269
  const fromInput = parseFromOption(options2.from);
16333
16270
  switch (fromInput.type) {
16334
16271
  case "local-profile":
16335
- await cloneFromLocalProfile(manager, name, fromInput.name, profileExists);
16272
+ await cloneFromLocalProfile(manager, name, fromInput.name);
16336
16273
  break;
16337
- case "local-path":
16338
- throw new ValidationError(`Local path installation is not yet implemented: "${fromInput.path}"
16339
-
16340
- ` + `Currently supported sources:
16341
- ` + ` - Existing profile: --from <profile-name>
16342
- ` + ` - Registry: --from <namespace>/<component>`);
16343
16274
  case "registry": {
16344
16275
  const { config: globalConfig, registryUrl } = await requireGlobalRegistry(fromInput.namespace);
16345
16276
  const registries = {};
@@ -16350,7 +16281,6 @@ async function runProfileAdd(name, options2) {
16350
16281
  namespace: fromInput.namespace,
16351
16282
  component: fromInput.component,
16352
16283
  profileName: name,
16353
- force: options2.force,
16354
16284
  registryUrl,
16355
16285
  registries
16356
16286
  });
@@ -16358,18 +16288,20 @@ async function runProfileAdd(name, options2) {
16358
16288
  }
16359
16289
  }
16360
16290
  }
16361
- async function createEmptyProfile(manager, name, exists) {
16291
+ async function createEmptyProfile(manager, name) {
16292
+ const exists = await manager.exists(name);
16362
16293
  if (exists) {
16363
- await manager.remove(name);
16294
+ throw new ProfileExistsError(name, `Remove it first with 'ocx profile rm ${name}'.`);
16364
16295
  }
16365
16296
  await manager.add(name);
16366
16297
  logger.success(`Created profile "${name}"`);
16367
16298
  }
16368
- async function cloneFromLocalProfile(manager, name, sourceName, exists) {
16369
- const source = await manager.get(sourceName);
16299
+ async function cloneFromLocalProfile(manager, name, sourceName) {
16300
+ const exists = await manager.exists(name);
16370
16301
  if (exists) {
16371
- await manager.remove(name);
16302
+ throw new ProfileExistsError(name, `Remove it first with 'ocx profile rm ${name}'.`);
16372
16303
  }
16304
+ const source = await manager.get(sourceName);
16373
16305
  await manager.add(name);
16374
16306
  await atomicWrite(getProfileOcxConfig(name), source.ocx);
16375
16307
  logger.success(`Created profile "${name}" (cloned from "${sourceName}")`);
@@ -17270,7 +17202,7 @@ function registerSelfUninstallCommand(parent) {
17270
17202
 
17271
17203
  // src/self-update/version-provider.ts
17272
17204
  class BuildTimeVersionProvider {
17273
- version = "1.4.3";
17205
+ version = "1.4.4";
17274
17206
  }
17275
17207
  var defaultVersionProvider = new BuildTimeVersionProvider;
17276
17208
 
@@ -17927,7 +17859,7 @@ function registerUpdateCheckHook(program2) {
17927
17859
  });
17928
17860
  }
17929
17861
  // src/index.ts
17930
- var version = "1.4.3";
17862
+ var version = "1.4.4";
17931
17863
  async function main2() {
17932
17864
  const program2 = new Command().name("ocx").description("OpenCode Extensions - Install agents, skills, plugins, and commands").version(version);
17933
17865
  registerInitCommand(program2);
@@ -17959,4 +17891,4 @@ export {
17959
17891
  buildRegistry
17960
17892
  };
17961
17893
 
17962
- //# debugId=947205F975538A0764756E2164756E21
17894
+ //# debugId=C29AE4337E16008A64756E2164756E21