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 +388 -20
- package/dist/index.js.map +11 -10
- package/package.json +1 -1
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/
|
|
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().
|
|
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()
|
|
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().
|
|
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
|
-
` + `
|
|
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
|
|
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
|
|
8784
|
-
const manifest = packument.versions[
|
|
8849
|
+
const resolvedVersion = version ?? packument["dist-tags"].latest;
|
|
8850
|
+
const manifest = packument.versions[resolvedVersion];
|
|
8785
8851
|
if (!manifest) {
|
|
8786
|
-
|
|
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}@${
|
|
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.
|
|
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=
|
|
14032
|
+
//# debugId=2706150244B1090964756E2164756E21
|