ocx 1.0.10 → 1.0.11
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 +311 -483
- package/dist/index.js.map +15 -12
- package/package.json +63 -62
package/dist/index.js
CHANGED
|
@@ -8486,76 +8486,7 @@ var coerce = {
|
|
|
8486
8486
|
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
|
8487
8487
|
};
|
|
8488
8488
|
var NEVER = INVALID;
|
|
8489
|
-
//
|
|
8490
|
-
var EXIT_CODES = {
|
|
8491
|
-
SUCCESS: 0,
|
|
8492
|
-
GENERAL: 1,
|
|
8493
|
-
NOT_FOUND: 66,
|
|
8494
|
-
NETWORK: 69,
|
|
8495
|
-
CONFIG: 78,
|
|
8496
|
-
INTEGRITY: 1
|
|
8497
|
-
};
|
|
8498
|
-
|
|
8499
|
-
class OCXError extends Error {
|
|
8500
|
-
code;
|
|
8501
|
-
exitCode;
|
|
8502
|
-
constructor(message, code, exitCode = EXIT_CODES.GENERAL) {
|
|
8503
|
-
super(message);
|
|
8504
|
-
this.code = code;
|
|
8505
|
-
this.exitCode = exitCode;
|
|
8506
|
-
this.name = "OCXError";
|
|
8507
|
-
}
|
|
8508
|
-
}
|
|
8509
|
-
|
|
8510
|
-
class NotFoundError extends OCXError {
|
|
8511
|
-
constructor(message) {
|
|
8512
|
-
super(message, "NOT_FOUND", EXIT_CODES.NOT_FOUND);
|
|
8513
|
-
this.name = "NotFoundError";
|
|
8514
|
-
}
|
|
8515
|
-
}
|
|
8516
|
-
|
|
8517
|
-
class NetworkError extends OCXError {
|
|
8518
|
-
constructor(message) {
|
|
8519
|
-
super(message, "NETWORK_ERROR", EXIT_CODES.NETWORK);
|
|
8520
|
-
this.name = "NetworkError";
|
|
8521
|
-
}
|
|
8522
|
-
}
|
|
8523
|
-
|
|
8524
|
-
class ConfigError extends OCXError {
|
|
8525
|
-
constructor(message) {
|
|
8526
|
-
super(message, "CONFIG_ERROR", EXIT_CODES.CONFIG);
|
|
8527
|
-
this.name = "ConfigError";
|
|
8528
|
-
}
|
|
8529
|
-
}
|
|
8530
|
-
|
|
8531
|
-
class ValidationError extends OCXError {
|
|
8532
|
-
constructor(message) {
|
|
8533
|
-
super(message, "VALIDATION_ERROR", EXIT_CODES.GENERAL);
|
|
8534
|
-
this.name = "ValidationError";
|
|
8535
|
-
}
|
|
8536
|
-
}
|
|
8537
|
-
|
|
8538
|
-
class ConflictError extends OCXError {
|
|
8539
|
-
constructor(message) {
|
|
8540
|
-
super(message, "CONFLICT", EXIT_CODES.GENERAL);
|
|
8541
|
-
this.name = "ConflictError";
|
|
8542
|
-
}
|
|
8543
|
-
}
|
|
8544
|
-
|
|
8545
|
-
class IntegrityError extends OCXError {
|
|
8546
|
-
constructor(component, expected, found) {
|
|
8547
|
-
const message = `Integrity verification failed for "${component}"
|
|
8548
|
-
` + ` Expected: ${expected}
|
|
8549
|
-
` + ` Found: ${found}
|
|
8550
|
-
|
|
8551
|
-
` + `The registry content has changed since this component was locked.
|
|
8552
|
-
` + `This could indicate tampering or an unauthorized update.`;
|
|
8553
|
-
super(message, "INTEGRITY_ERROR", EXIT_CODES.INTEGRITY);
|
|
8554
|
-
this.name = "IntegrityError";
|
|
8555
|
-
}
|
|
8556
|
-
}
|
|
8557
|
-
|
|
8558
|
-
// src/schemas/registry.ts
|
|
8489
|
+
// ../schemas/dist/registry.js
|
|
8559
8490
|
var openCodeNameSchema = exports_external.string().min(1, "Name cannot be empty").max(64, "Name cannot exceed 64 characters").regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, {
|
|
8560
8491
|
message: "Must be lowercase alphanumeric with single hyphen separators (e.g., 'my-component', 'my-plugin'). Cannot start/end with hyphen or have consecutive hyphens."
|
|
8561
8492
|
});
|
|
@@ -8565,11 +8496,11 @@ var qualifiedComponentSchema = exports_external.string().regex(/^[a-z0-9]+(-[a-z
|
|
|
8565
8496
|
});
|
|
8566
8497
|
function parseQualifiedComponent(ref) {
|
|
8567
8498
|
if (!ref.includes("/")) {
|
|
8568
|
-
throw new
|
|
8499
|
+
throw new Error(`Invalid component reference: "${ref}". Use format: namespace/component`);
|
|
8569
8500
|
}
|
|
8570
8501
|
const [namespace, component] = ref.split("/");
|
|
8571
8502
|
if (!namespace || !component) {
|
|
8572
|
-
throw new
|
|
8503
|
+
throw new Error(`Invalid component reference: "${ref}". Both namespace and component are required.`);
|
|
8573
8504
|
}
|
|
8574
8505
|
return { namespace, component };
|
|
8575
8506
|
}
|
|
@@ -8602,9 +8533,6 @@ var targetPathSchema = exports_external.string().refine((path) => path.startsWit
|
|
|
8602
8533
|
}, {
|
|
8603
8534
|
message: 'Target must be in a valid directory: ".opencode/{agent|skill|plugin|command|tool|philosophy}/..."'
|
|
8604
8535
|
});
|
|
8605
|
-
var skillTargetSchema = exports_external.string().regex(/^\.opencode\/skill\/[a-z0-9]+(-[a-z0-9]+)*\/SKILL\.md$/, {
|
|
8606
|
-
message: 'Skill target must match pattern ".opencode/skill/<name>/SKILL.md" where name follows OpenCode naming rules'
|
|
8607
|
-
});
|
|
8608
8536
|
var mcpServerObjectSchema = exports_external.object({
|
|
8609
8537
|
type: exports_external.enum(["remote", "local"]),
|
|
8610
8538
|
url: exports_external.string().url().optional(),
|
|
@@ -8640,11 +8568,18 @@ var agentConfigSchema = exports_external.object({
|
|
|
8640
8568
|
prompt: exports_external.string().optional(),
|
|
8641
8569
|
permission: exports_external.record(exports_external.enum(["allow", "deny"])).optional()
|
|
8642
8570
|
});
|
|
8571
|
+
var permissionConfigSchema = exports_external.object({
|
|
8572
|
+
bash: exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))]).optional(),
|
|
8573
|
+
edit: exports_external.union([exports_external.enum(["ask", "allow", "deny"]), exports_external.record(exports_external.enum(["ask", "allow", "deny"]))]).optional(),
|
|
8574
|
+
mcp: exports_external.record(exports_external.enum(["ask", "allow", "deny"])).optional()
|
|
8575
|
+
});
|
|
8643
8576
|
var opencodeConfigSchema = exports_external.object({
|
|
8644
|
-
|
|
8577
|
+
mcp: exports_external.record(mcpServerRefSchema).optional(),
|
|
8578
|
+
plugin: exports_external.array(exports_external.string()).optional(),
|
|
8645
8579
|
tools: exports_external.record(exports_external.boolean()).optional(),
|
|
8646
8580
|
agent: exports_external.record(agentConfigSchema).optional(),
|
|
8647
|
-
instructions: exports_external.array(exports_external.string()).optional()
|
|
8581
|
+
instructions: exports_external.array(exports_external.string()).optional(),
|
|
8582
|
+
permission: permissionConfigSchema.optional()
|
|
8648
8583
|
});
|
|
8649
8584
|
var componentManifestSchema = exports_external.object({
|
|
8650
8585
|
name: openCodeNameSchema,
|
|
@@ -8654,10 +8589,7 @@ var componentManifestSchema = exports_external.object({
|
|
|
8654
8589
|
dependencies: exports_external.array(dependencyRefSchema).default([]),
|
|
8655
8590
|
npmDependencies: exports_external.array(exports_external.string()).optional(),
|
|
8656
8591
|
npmDevDependencies: exports_external.array(exports_external.string()).optional(),
|
|
8657
|
-
|
|
8658
|
-
mcpScope: exports_external.enum(["agent", "global"]).default("agent"),
|
|
8659
|
-
opencode: opencodeConfigSchema.optional(),
|
|
8660
|
-
disabledTools: exports_external.array(exports_external.string()).optional()
|
|
8592
|
+
opencode: opencodeConfigSchema.optional()
|
|
8661
8593
|
});
|
|
8662
8594
|
function inferTargetPath(sourcePath) {
|
|
8663
8595
|
return `.opencode/${sourcePath}`;
|
|
@@ -8682,13 +8614,20 @@ function normalizeMcpServer(server) {
|
|
|
8682
8614
|
return server;
|
|
8683
8615
|
}
|
|
8684
8616
|
function normalizeComponentManifest(manifest) {
|
|
8617
|
+
let normalizedOpencode;
|
|
8618
|
+
if (manifest.opencode) {
|
|
8619
|
+
const { mcp, ...rest } = manifest.opencode;
|
|
8620
|
+
normalizedOpencode = {
|
|
8621
|
+
...rest,
|
|
8622
|
+
...mcp && {
|
|
8623
|
+
mcp: Object.fromEntries(Object.entries(mcp).map(([name, server]) => [name, normalizeMcpServer(server)]))
|
|
8624
|
+
}
|
|
8625
|
+
};
|
|
8626
|
+
}
|
|
8685
8627
|
return {
|
|
8686
8628
|
...manifest,
|
|
8687
8629
|
files: manifest.files.map(normalizeFile),
|
|
8688
|
-
|
|
8689
|
-
name,
|
|
8690
|
-
normalizeMcpServer(server)
|
|
8691
|
-
])) : undefined
|
|
8630
|
+
opencode: normalizedOpencode
|
|
8692
8631
|
};
|
|
8693
8632
|
}
|
|
8694
8633
|
var semverRegex = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/;
|
|
@@ -8732,6 +8671,97 @@ var registryIndexSchema = exports_external.object({
|
|
|
8732
8671
|
}))
|
|
8733
8672
|
});
|
|
8734
8673
|
|
|
8674
|
+
// ../schemas/dist/config.js
|
|
8675
|
+
var registryConfigSchema = exports_external.object({
|
|
8676
|
+
url: exports_external.string().url("Registry URL must be a valid URL"),
|
|
8677
|
+
version: exports_external.string().optional(),
|
|
8678
|
+
headers: exports_external.record(exports_external.string()).optional()
|
|
8679
|
+
});
|
|
8680
|
+
var ocxConfigSchema = exports_external.object({
|
|
8681
|
+
$schema: exports_external.string().optional(),
|
|
8682
|
+
registries: exports_external.record(registryConfigSchema).default({}),
|
|
8683
|
+
lockRegistries: exports_external.boolean().default(false)
|
|
8684
|
+
});
|
|
8685
|
+
var installedComponentSchema = exports_external.object({
|
|
8686
|
+
registry: exports_external.string(),
|
|
8687
|
+
version: exports_external.string(),
|
|
8688
|
+
hash: exports_external.string(),
|
|
8689
|
+
files: exports_external.array(exports_external.string()),
|
|
8690
|
+
installedAt: exports_external.string()
|
|
8691
|
+
});
|
|
8692
|
+
var ocxLockSchema = exports_external.object({
|
|
8693
|
+
lockVersion: exports_external.literal(1),
|
|
8694
|
+
installed: exports_external.record(qualifiedComponentSchema, installedComponentSchema).default({})
|
|
8695
|
+
});
|
|
8696
|
+
// src/utils/errors.ts
|
|
8697
|
+
var EXIT_CODES = {
|
|
8698
|
+
SUCCESS: 0,
|
|
8699
|
+
GENERAL: 1,
|
|
8700
|
+
NOT_FOUND: 66,
|
|
8701
|
+
NETWORK: 69,
|
|
8702
|
+
CONFIG: 78,
|
|
8703
|
+
INTEGRITY: 1
|
|
8704
|
+
};
|
|
8705
|
+
|
|
8706
|
+
class OCXError extends Error {
|
|
8707
|
+
code;
|
|
8708
|
+
exitCode;
|
|
8709
|
+
constructor(message, code, exitCode = EXIT_CODES.GENERAL) {
|
|
8710
|
+
super(message);
|
|
8711
|
+
this.code = code;
|
|
8712
|
+
this.exitCode = exitCode;
|
|
8713
|
+
this.name = "OCXError";
|
|
8714
|
+
}
|
|
8715
|
+
}
|
|
8716
|
+
|
|
8717
|
+
class NotFoundError extends OCXError {
|
|
8718
|
+
constructor(message) {
|
|
8719
|
+
super(message, "NOT_FOUND", EXIT_CODES.NOT_FOUND);
|
|
8720
|
+
this.name = "NotFoundError";
|
|
8721
|
+
}
|
|
8722
|
+
}
|
|
8723
|
+
|
|
8724
|
+
class NetworkError extends OCXError {
|
|
8725
|
+
constructor(message) {
|
|
8726
|
+
super(message, "NETWORK_ERROR", EXIT_CODES.NETWORK);
|
|
8727
|
+
this.name = "NetworkError";
|
|
8728
|
+
}
|
|
8729
|
+
}
|
|
8730
|
+
|
|
8731
|
+
class ConfigError extends OCXError {
|
|
8732
|
+
constructor(message) {
|
|
8733
|
+
super(message, "CONFIG_ERROR", EXIT_CODES.CONFIG);
|
|
8734
|
+
this.name = "ConfigError";
|
|
8735
|
+
}
|
|
8736
|
+
}
|
|
8737
|
+
|
|
8738
|
+
class ValidationError extends OCXError {
|
|
8739
|
+
constructor(message) {
|
|
8740
|
+
super(message, "VALIDATION_ERROR", EXIT_CODES.GENERAL);
|
|
8741
|
+
this.name = "ValidationError";
|
|
8742
|
+
}
|
|
8743
|
+
}
|
|
8744
|
+
|
|
8745
|
+
class ConflictError extends OCXError {
|
|
8746
|
+
constructor(message) {
|
|
8747
|
+
super(message, "CONFLICT", EXIT_CODES.GENERAL);
|
|
8748
|
+
this.name = "ConflictError";
|
|
8749
|
+
}
|
|
8750
|
+
}
|
|
8751
|
+
|
|
8752
|
+
class IntegrityError extends OCXError {
|
|
8753
|
+
constructor(component, expected, found) {
|
|
8754
|
+
const message = `Integrity verification failed for "${component}"
|
|
8755
|
+
` + ` Expected: ${expected}
|
|
8756
|
+
` + ` Found: ${found}
|
|
8757
|
+
|
|
8758
|
+
` + `The registry content has changed since this component was locked.
|
|
8759
|
+
` + `This could indicate tampering or an unauthorized update.`;
|
|
8760
|
+
super(message, "INTEGRITY_ERROR", EXIT_CODES.INTEGRITY);
|
|
8761
|
+
this.name = "IntegrityError";
|
|
8762
|
+
}
|
|
8763
|
+
}
|
|
8764
|
+
|
|
8735
8765
|
// src/registry/fetcher.ts
|
|
8736
8766
|
var cache = new Map;
|
|
8737
8767
|
async function fetchWithCache(url, parse) {
|
|
@@ -8835,6 +8865,18 @@ function r(e3, n2) {
|
|
|
8835
8865
|
return i;
|
|
8836
8866
|
}
|
|
8837
8867
|
|
|
8868
|
+
// src/registry/merge.ts
|
|
8869
|
+
function mergeOpencodeConfig(target, source) {
|
|
8870
|
+
const merged = n(target, source);
|
|
8871
|
+
if (target.plugin && source.plugin) {
|
|
8872
|
+
merged.plugin = Array.from(new Set([...target.plugin, ...source.plugin]));
|
|
8873
|
+
}
|
|
8874
|
+
if (target.instructions && source.instructions) {
|
|
8875
|
+
merged.instructions = Array.from(new Set([...target.instructions, ...source.instructions]));
|
|
8876
|
+
}
|
|
8877
|
+
return merged;
|
|
8878
|
+
}
|
|
8879
|
+
|
|
8838
8880
|
// src/registry/resolver.ts
|
|
8839
8881
|
function parseComponentRef(ref, defaultNamespace) {
|
|
8840
8882
|
if (ref.includes("/")) {
|
|
@@ -8848,14 +8890,9 @@ function parseComponentRef(ref, defaultNamespace) {
|
|
|
8848
8890
|
async function resolveDependencies(registries, componentNames) {
|
|
8849
8891
|
const resolved = new Map;
|
|
8850
8892
|
const visiting = new Set;
|
|
8851
|
-
const mcpServers = {};
|
|
8852
|
-
const agentMcpBindings = [];
|
|
8853
8893
|
const npmDeps = new Set;
|
|
8854
8894
|
const npmDevDeps = new Set;
|
|
8855
|
-
|
|
8856
|
-
const plugins = new Set;
|
|
8857
|
-
const agentConfigs = {};
|
|
8858
|
-
const instructionsSet = new Set;
|
|
8895
|
+
let opencode = {};
|
|
8859
8896
|
async function resolve(componentNamespace, componentName, path = []) {
|
|
8860
8897
|
const qualifiedName = createQualifiedComponent(componentNamespace, componentName);
|
|
8861
8898
|
if (resolved.has(qualifiedName)) {
|
|
@@ -8889,20 +8926,6 @@ async function resolveDependencies(registries, componentNames) {
|
|
|
8889
8926
|
qualifiedName
|
|
8890
8927
|
});
|
|
8891
8928
|
visiting.delete(qualifiedName);
|
|
8892
|
-
if (normalizedComponent.mcpServers) {
|
|
8893
|
-
const serverNames = [];
|
|
8894
|
-
for (const [serverName, config] of Object.entries(normalizedComponent.mcpServers)) {
|
|
8895
|
-
mcpServers[serverName] = config;
|
|
8896
|
-
serverNames.push(serverName);
|
|
8897
|
-
}
|
|
8898
|
-
const scope = component.mcpScope ?? "agent";
|
|
8899
|
-
if (component.type === "ocx:agent" && scope === "agent" && serverNames.length > 0) {
|
|
8900
|
-
agentMcpBindings.push({
|
|
8901
|
-
agentName: component.name,
|
|
8902
|
-
serverNames
|
|
8903
|
-
});
|
|
8904
|
-
}
|
|
8905
|
-
}
|
|
8906
8929
|
if (component.npmDependencies) {
|
|
8907
8930
|
for (const dep of component.npmDependencies) {
|
|
8908
8931
|
npmDeps.add(dep);
|
|
@@ -8913,34 +8936,8 @@ async function resolveDependencies(registries, componentNames) {
|
|
|
8913
8936
|
npmDevDeps.add(dep);
|
|
8914
8937
|
}
|
|
8915
8938
|
}
|
|
8916
|
-
if (
|
|
8917
|
-
|
|
8918
|
-
disabledTools.add(tool);
|
|
8919
|
-
}
|
|
8920
|
-
}
|
|
8921
|
-
if (component.opencode) {
|
|
8922
|
-
if (component.opencode.plugins) {
|
|
8923
|
-
for (const plugin of component.opencode.plugins) {
|
|
8924
|
-
plugins.add(plugin);
|
|
8925
|
-
}
|
|
8926
|
-
}
|
|
8927
|
-
if (component.opencode.agent) {
|
|
8928
|
-
for (const [agentName, config] of Object.entries(component.opencode.agent)) {
|
|
8929
|
-
agentConfigs[agentName] = n(agentConfigs[agentName] ?? {}, config);
|
|
8930
|
-
}
|
|
8931
|
-
}
|
|
8932
|
-
if (component.opencode.instructions) {
|
|
8933
|
-
for (const instruction of component.opencode.instructions) {
|
|
8934
|
-
instructionsSet.add(instruction);
|
|
8935
|
-
}
|
|
8936
|
-
}
|
|
8937
|
-
if (component.opencode.tools) {
|
|
8938
|
-
for (const [tool, enabled] of Object.entries(component.opencode.tools)) {
|
|
8939
|
-
if (enabled === false) {
|
|
8940
|
-
disabledTools.add(tool);
|
|
8941
|
-
}
|
|
8942
|
-
}
|
|
8943
|
-
}
|
|
8939
|
+
if (normalizedComponent.opencode) {
|
|
8940
|
+
opencode = mergeOpencodeConfig(opencode, normalizedComponent.opencode);
|
|
8944
8941
|
}
|
|
8945
8942
|
}
|
|
8946
8943
|
for (const name of componentNames) {
|
|
@@ -8952,14 +8949,9 @@ async function resolveDependencies(registries, componentNames) {
|
|
|
8952
8949
|
return {
|
|
8953
8950
|
components,
|
|
8954
8951
|
installOrder,
|
|
8955
|
-
mcpServers,
|
|
8956
|
-
agentMcpBindings,
|
|
8957
8952
|
npmDependencies: Array.from(npmDeps),
|
|
8958
8953
|
npmDevDependencies: Array.from(npmDevDeps),
|
|
8959
|
-
|
|
8960
|
-
plugins: Array.from(plugins),
|
|
8961
|
-
agentConfigs,
|
|
8962
|
-
instructions: Array.from(instructionsSet)
|
|
8954
|
+
opencode
|
|
8963
8955
|
};
|
|
8964
8956
|
}
|
|
8965
8957
|
|
|
@@ -10287,44 +10279,6 @@ function applyEdits(text, edits) {
|
|
|
10287
10279
|
}
|
|
10288
10280
|
|
|
10289
10281
|
// src/schemas/config.ts
|
|
10290
|
-
var registryConfigSchema = exports_external.object({
|
|
10291
|
-
url: exports_external.string().url("Registry URL must be a valid URL"),
|
|
10292
|
-
version: exports_external.string().optional(),
|
|
10293
|
-
headers: exports_external.record(exports_external.string()).optional()
|
|
10294
|
-
});
|
|
10295
|
-
var ocxConfigSchema = exports_external.object({
|
|
10296
|
-
$schema: exports_external.string().optional(),
|
|
10297
|
-
registries: exports_external.record(registryConfigSchema).default({}),
|
|
10298
|
-
lockRegistries: exports_external.boolean().default(false)
|
|
10299
|
-
});
|
|
10300
|
-
var installedComponentSchema = exports_external.object({
|
|
10301
|
-
registry: exports_external.string(),
|
|
10302
|
-
version: exports_external.string(),
|
|
10303
|
-
hash: exports_external.string(),
|
|
10304
|
-
files: exports_external.array(exports_external.string()),
|
|
10305
|
-
installedAt: exports_external.string(),
|
|
10306
|
-
alias: exports_external.string().optional()
|
|
10307
|
-
});
|
|
10308
|
-
var ocxLockSchema = exports_external.object({
|
|
10309
|
-
lockVersion: exports_external.literal(1),
|
|
10310
|
-
installed: exports_external.record(qualifiedComponentSchema, installedComponentSchema).default({})
|
|
10311
|
-
});
|
|
10312
|
-
var opencodeMcpSchema = exports_external.record(mcpServerRefSchema);
|
|
10313
|
-
var opencodeAgentSchema = exports_external.object({
|
|
10314
|
-
disable: exports_external.boolean().optional(),
|
|
10315
|
-
tools: exports_external.record(exports_external.boolean()).optional(),
|
|
10316
|
-
temperature: exports_external.number().min(0).max(2).optional(),
|
|
10317
|
-
prompt: exports_external.string().optional(),
|
|
10318
|
-
permission: exports_external.record(exports_external.enum(["allow", "deny"])).optional()
|
|
10319
|
-
});
|
|
10320
|
-
var opencodeConfigPatchSchema = exports_external.object({
|
|
10321
|
-
default_agent: exports_external.string().optional(),
|
|
10322
|
-
mcp: opencodeMcpSchema.optional(),
|
|
10323
|
-
tools: exports_external.record(exports_external.boolean()).optional(),
|
|
10324
|
-
agent: exports_external.record(opencodeAgentSchema).optional(),
|
|
10325
|
-
plugin: exports_external.array(exports_external.string()).optional(),
|
|
10326
|
-
instructions: exports_external.array(exports_external.string()).optional()
|
|
10327
|
-
});
|
|
10328
10282
|
var CONFIG_FILE = "ocx.jsonc";
|
|
10329
10283
|
var LOCK_FILE = "ocx.lock";
|
|
10330
10284
|
async function readOcxConfig(cwd) {
|
|
@@ -10342,9 +10296,9 @@ async function readOcxConfig(cwd) {
|
|
|
10342
10296
|
throw error;
|
|
10343
10297
|
}
|
|
10344
10298
|
}
|
|
10345
|
-
async function writeOcxConfig(cwd,
|
|
10299
|
+
async function writeOcxConfig(cwd, config2) {
|
|
10346
10300
|
const configPath = `${cwd}/${CONFIG_FILE}`;
|
|
10347
|
-
const content = JSON.stringify(
|
|
10301
|
+
const content = JSON.stringify(config2, null, 2);
|
|
10348
10302
|
await Bun.write(configPath, content);
|
|
10349
10303
|
}
|
|
10350
10304
|
async function readOcxLock(cwd) {
|
|
@@ -10367,7 +10321,7 @@ var JSONC_OPTIONS = {
|
|
|
10367
10321
|
`
|
|
10368
10322
|
}
|
|
10369
10323
|
};
|
|
10370
|
-
async function
|
|
10324
|
+
async function readOpencodeJsonConfig(cwd) {
|
|
10371
10325
|
const jsonPath = `${cwd}/opencode.json`;
|
|
10372
10326
|
const jsoncPath = `${cwd}/opencode.jsonc`;
|
|
10373
10327
|
for (const configPath of [jsoncPath, jsonPath]) {
|
|
@@ -10383,224 +10337,62 @@ async function readOpencodeConfig(cwd) {
|
|
|
10383
10337
|
}
|
|
10384
10338
|
return null;
|
|
10385
10339
|
}
|
|
10386
|
-
async function
|
|
10340
|
+
async function writeOpencodeJsonConfig(path, content) {
|
|
10387
10341
|
await Bun.write(path, content);
|
|
10388
10342
|
}
|
|
10389
|
-
function
|
|
10390
|
-
|
|
10391
|
-
const skipped = [];
|
|
10392
|
-
let updatedContent = content;
|
|
10393
|
-
const existingMcp = config.mcp ?? {};
|
|
10394
|
-
for (const [name, server] of Object.entries(mcpServers)) {
|
|
10395
|
-
if (existingMcp[name]) {
|
|
10396
|
-
skipped.push(name);
|
|
10397
|
-
continue;
|
|
10398
|
-
}
|
|
10399
|
-
const serverConfig = {
|
|
10400
|
-
type: server.type
|
|
10401
|
-
};
|
|
10402
|
-
if (server.type === "remote" && server.url) {
|
|
10403
|
-
serverConfig.url = server.url;
|
|
10404
|
-
}
|
|
10405
|
-
if (server.type === "local" && server.command) {
|
|
10406
|
-
serverConfig.command = server.command;
|
|
10407
|
-
}
|
|
10408
|
-
if (server.args) {
|
|
10409
|
-
serverConfig.args = server.args;
|
|
10410
|
-
}
|
|
10411
|
-
if (server.headers) {
|
|
10412
|
-
serverConfig.headers = server.headers;
|
|
10413
|
-
}
|
|
10414
|
-
if (server.oauth !== undefined) {
|
|
10415
|
-
serverConfig.oauth = server.oauth;
|
|
10416
|
-
}
|
|
10417
|
-
if (server.enabled !== undefined) {
|
|
10418
|
-
serverConfig.enabled = server.enabled;
|
|
10419
|
-
}
|
|
10420
|
-
if (server.environment) {
|
|
10421
|
-
serverConfig.environment = server.environment;
|
|
10422
|
-
}
|
|
10423
|
-
const edits = modify(updatedContent, ["mcp", name], serverConfig, JSONC_OPTIONS);
|
|
10424
|
-
updatedContent = applyEdits(updatedContent, edits);
|
|
10425
|
-
added.push(name);
|
|
10426
|
-
}
|
|
10427
|
-
return { content: updatedContent, added, skipped };
|
|
10428
|
-
}
|
|
10429
|
-
function applyGlobalToolDisables(content, serverNames) {
|
|
10430
|
-
let updatedContent = content;
|
|
10431
|
-
for (const name of serverNames) {
|
|
10432
|
-
const toolPattern = `${name}_*`;
|
|
10433
|
-
const edits = modify(updatedContent, ["tools", toolPattern], false, JSONC_OPTIONS);
|
|
10434
|
-
updatedContent = applyEdits(updatedContent, edits);
|
|
10435
|
-
}
|
|
10436
|
-
return updatedContent;
|
|
10437
|
-
}
|
|
10438
|
-
function applyDisabledTools(content, toolNames) {
|
|
10439
|
-
let updatedContent = content;
|
|
10440
|
-
for (const name of toolNames) {
|
|
10441
|
-
const edits = modify(updatedContent, ["tools", name], false, JSONC_OPTIONS);
|
|
10442
|
-
updatedContent = applyEdits(updatedContent, edits);
|
|
10443
|
-
}
|
|
10444
|
-
return updatedContent;
|
|
10445
|
-
}
|
|
10446
|
-
function applyAgentToolEnables(content, bindings) {
|
|
10447
|
-
let updatedContent = content;
|
|
10448
|
-
const agentsConfigured = [];
|
|
10449
|
-
for (const binding of bindings) {
|
|
10450
|
-
for (const serverName of binding.serverNames) {
|
|
10451
|
-
const toolPattern = `${serverName}_*`;
|
|
10452
|
-
const edits = modify(updatedContent, ["agent", binding.agentName, "tools", toolPattern], true, JSONC_OPTIONS);
|
|
10453
|
-
updatedContent = applyEdits(updatedContent, edits);
|
|
10454
|
-
}
|
|
10455
|
-
if (binding.serverNames.length > 0) {
|
|
10456
|
-
agentsConfigured.push(binding.agentName);
|
|
10457
|
-
}
|
|
10458
|
-
}
|
|
10459
|
-
return { content: updatedContent, agentsConfigured: [...new Set(agentsConfigured)] };
|
|
10460
|
-
}
|
|
10461
|
-
function applyDefaultAgent(content, config, defaultAgent) {
|
|
10462
|
-
if (config.default_agent) {
|
|
10343
|
+
function applyValueAtPath(content, path, value) {
|
|
10344
|
+
if (value === null || value === undefined) {
|
|
10463
10345
|
return content;
|
|
10464
10346
|
}
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
const added = [];
|
|
10470
|
-
let updatedContent = content;
|
|
10471
|
-
const existingPlugins = config.plugin ?? [];
|
|
10472
|
-
for (const plugin of plugins) {
|
|
10473
|
-
if (existingPlugins.includes(plugin)) {
|
|
10474
|
-
continue;
|
|
10347
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
10348
|
+
let updatedContent = content;
|
|
10349
|
+
for (const [key, val] of Object.entries(value)) {
|
|
10350
|
+
updatedContent = applyValueAtPath(updatedContent, [...path, key], val);
|
|
10475
10351
|
}
|
|
10476
|
-
|
|
10477
|
-
const edits = modify(updatedContent, ["plugin", newIndex], plugin, JSONC_OPTIONS);
|
|
10478
|
-
updatedContent = applyEdits(updatedContent, edits);
|
|
10479
|
-
added.push(plugin);
|
|
10352
|
+
return updatedContent;
|
|
10480
10353
|
}
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
let updatedContent = content;
|
|
10485
|
-
const agentsConfigured = [];
|
|
10486
|
-
for (const [agentName, componentAgentConfig] of Object.entries(agentConfigs)) {
|
|
10487
|
-
const existingAgentConfig = existingConfig.agent?.[agentName] ?? {};
|
|
10488
|
-
const merged = n(componentAgentConfig, existingAgentConfig);
|
|
10489
|
-
if (merged.tools) {
|
|
10490
|
-
for (const [toolPattern, enabled] of Object.entries(merged.tools)) {
|
|
10491
|
-
const edits = modify(updatedContent, ["agent", agentName, "tools", toolPattern], enabled, JSONC_OPTIONS);
|
|
10492
|
-
updatedContent = applyEdits(updatedContent, edits);
|
|
10493
|
-
}
|
|
10494
|
-
}
|
|
10495
|
-
if (merged.temperature !== undefined) {
|
|
10496
|
-
const edits = modify(updatedContent, ["agent", agentName, "temperature"], merged.temperature, JSONC_OPTIONS);
|
|
10497
|
-
updatedContent = applyEdits(updatedContent, edits);
|
|
10498
|
-
}
|
|
10499
|
-
if (merged.prompt) {
|
|
10500
|
-
const edits = modify(updatedContent, ["agent", agentName, "prompt"], merged.prompt, JSONC_OPTIONS);
|
|
10501
|
-
updatedContent = applyEdits(updatedContent, edits);
|
|
10502
|
-
}
|
|
10503
|
-
if (merged.permission) {
|
|
10504
|
-
for (const [pattern, permission] of Object.entries(merged.permission)) {
|
|
10505
|
-
const edits = modify(updatedContent, ["agent", agentName, "permission", pattern], permission, JSONC_OPTIONS);
|
|
10506
|
-
updatedContent = applyEdits(updatedContent, edits);
|
|
10507
|
-
}
|
|
10508
|
-
}
|
|
10509
|
-
agentsConfigured.push(agentName);
|
|
10510
|
-
}
|
|
10511
|
-
return { content: updatedContent, agentsConfigured };
|
|
10512
|
-
}
|
|
10513
|
-
function applyInstructions(content, config, instructions) {
|
|
10514
|
-
const added = [];
|
|
10515
|
-
let updatedContent = content;
|
|
10516
|
-
const existingInstructions = config.instructions ?? [];
|
|
10517
|
-
for (const instruction of instructions) {
|
|
10518
|
-
if (existingInstructions.includes(instruction)) {
|
|
10519
|
-
continue;
|
|
10520
|
-
}
|
|
10521
|
-
const newIndex = existingInstructions.length + added.length;
|
|
10522
|
-
const edits = modify(updatedContent, ["instructions", newIndex], instruction, JSONC_OPTIONS);
|
|
10523
|
-
updatedContent = applyEdits(updatedContent, edits);
|
|
10524
|
-
added.push(instruction);
|
|
10354
|
+
if (Array.isArray(value)) {
|
|
10355
|
+
const edits2 = modify(content, path, value, JSONC_OPTIONS);
|
|
10356
|
+
return applyEdits(content, edits2);
|
|
10525
10357
|
}
|
|
10526
|
-
|
|
10358
|
+
const edits = modify(content, path, value, JSONC_OPTIONS);
|
|
10359
|
+
return applyEdits(content, edits);
|
|
10527
10360
|
}
|
|
10528
|
-
async function
|
|
10529
|
-
const existing = await
|
|
10361
|
+
async function updateOpencodeJsonConfig(cwd, opencode) {
|
|
10362
|
+
const existing = await readOpencodeJsonConfig(cwd);
|
|
10530
10363
|
let content;
|
|
10531
|
-
let config;
|
|
10532
10364
|
let configPath;
|
|
10533
10365
|
let created = false;
|
|
10534
10366
|
if (existing) {
|
|
10535
10367
|
content = existing.content;
|
|
10536
|
-
config = existing.config;
|
|
10537
10368
|
configPath = existing.path;
|
|
10538
10369
|
} else {
|
|
10539
|
-
|
|
10540
|
-
content = JSON.stringify(
|
|
10541
|
-
configPath = `${cwd}/opencode.
|
|
10370
|
+
const config2 = { $schema: "https://opencode.ai/config.json" };
|
|
10371
|
+
content = JSON.stringify(config2, null, "\t");
|
|
10372
|
+
configPath = `${cwd}/opencode.jsonc`;
|
|
10542
10373
|
created = true;
|
|
10543
10374
|
}
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
if (options.mcpServers && Object.keys(options.mcpServers).length > 0) {
|
|
10551
|
-
const result = applyMcpServers(content, config, options.mcpServers);
|
|
10552
|
-
content = result.content;
|
|
10553
|
-
mcpAdded = result.added;
|
|
10554
|
-
mcpSkipped = result.skipped;
|
|
10555
|
-
}
|
|
10556
|
-
if (options.agentMcpBindings && options.agentMcpBindings.length > 0) {
|
|
10557
|
-
const allScopedServers = [...new Set(options.agentMcpBindings.flatMap((b) => b.serverNames))];
|
|
10558
|
-
if (allScopedServers.length > 0) {
|
|
10559
|
-
content = applyGlobalToolDisables(content, allScopedServers);
|
|
10560
|
-
const agentResult = applyAgentToolEnables(content, options.agentMcpBindings);
|
|
10561
|
-
content = agentResult.content;
|
|
10562
|
-
agentsConfigured = agentResult.agentsConfigured;
|
|
10563
|
-
}
|
|
10564
|
-
}
|
|
10565
|
-
if (options.defaultAgent) {
|
|
10566
|
-
const updatedConfig = parse2(content, [], { allowTrailingComma: true });
|
|
10567
|
-
content = applyDefaultAgent(content, updatedConfig, options.defaultAgent);
|
|
10568
|
-
}
|
|
10569
|
-
if (options.disabledTools && options.disabledTools.length > 0) {
|
|
10570
|
-
content = applyDisabledTools(content, options.disabledTools);
|
|
10571
|
-
toolsDisabled = options.disabledTools;
|
|
10572
|
-
}
|
|
10573
|
-
if (options.plugins && options.plugins.length > 0) {
|
|
10574
|
-
const updatedConfig = parse2(content, [], { allowTrailingComma: true });
|
|
10575
|
-
const result = applyPlugins(content, updatedConfig, options.plugins);
|
|
10576
|
-
content = result.content;
|
|
10577
|
-
pluginsAdded = result.added;
|
|
10578
|
-
}
|
|
10579
|
-
if (options.agentConfigs && Object.keys(options.agentConfigs).length > 0) {
|
|
10580
|
-
const updatedConfig = parse2(content, [], { allowTrailingComma: true });
|
|
10581
|
-
const result = applyAgentConfigs(content, updatedConfig, options.agentConfigs);
|
|
10582
|
-
content = result.content;
|
|
10583
|
-
agentsConfigured = [...new Set([...agentsConfigured, ...result.agentsConfigured])];
|
|
10584
|
-
}
|
|
10585
|
-
if (options.instructions && options.instructions.length > 0) {
|
|
10586
|
-
const updatedConfig = parse2(content, [], { allowTrailingComma: true });
|
|
10587
|
-
const result = applyInstructions(content, updatedConfig, options.instructions);
|
|
10588
|
-
content = result.content;
|
|
10589
|
-
instructionsAdded = result.added;
|
|
10590
|
-
}
|
|
10591
|
-
await writeOpencodeConfig(configPath, content);
|
|
10375
|
+
const originalContent = content;
|
|
10376
|
+
content = applyValueAtPath(content, [], opencode);
|
|
10377
|
+
const changed = content !== originalContent;
|
|
10378
|
+
if (changed) {
|
|
10379
|
+
await writeOpencodeJsonConfig(configPath, content);
|
|
10380
|
+
}
|
|
10592
10381
|
return {
|
|
10593
10382
|
path: configPath,
|
|
10594
10383
|
created,
|
|
10595
|
-
|
|
10596
|
-
mcpSkipped,
|
|
10597
|
-
agentsConfigured,
|
|
10598
|
-
toolsDisabled,
|
|
10599
|
-
pluginsAdded,
|
|
10600
|
-
instructionsAdded
|
|
10384
|
+
changed
|
|
10601
10385
|
};
|
|
10602
10386
|
}
|
|
10603
10387
|
|
|
10388
|
+
// src/utils/content.ts
|
|
10389
|
+
function normalizeContent(content) {
|
|
10390
|
+
return content.replace(/\r\n/g, `
|
|
10391
|
+
`).trim();
|
|
10392
|
+
}
|
|
10393
|
+
function isContentIdentical(existing, incoming) {
|
|
10394
|
+
return normalizeContent(existing) === normalizeContent(incoming);
|
|
10395
|
+
}
|
|
10604
10396
|
// src/utils/env.ts
|
|
10605
10397
|
var isCI = Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.JENKINS_URL || process.env.BUILDKITE);
|
|
10606
10398
|
var isTTY = Boolean(process.stdout.isTTY && !isCI);
|
|
@@ -12140,8 +11932,8 @@ function registerAddCommand(program2) {
|
|
|
12140
11932
|
async function runAdd(componentNames, options2) {
|
|
12141
11933
|
const cwd = options2.cwd ?? process.cwd();
|
|
12142
11934
|
const lockPath = join(cwd, "ocx.lock");
|
|
12143
|
-
const
|
|
12144
|
-
if (!
|
|
11935
|
+
const config2 = await readOcxConfig(cwd);
|
|
11936
|
+
if (!config2) {
|
|
12145
11937
|
throw new ConfigError("No ocx.jsonc found. Run 'ocx init' first.");
|
|
12146
11938
|
}
|
|
12147
11939
|
let lock = { lockVersion: 1, installed: {} };
|
|
@@ -12152,7 +11944,7 @@ async function runAdd(componentNames, options2) {
|
|
|
12152
11944
|
const spin = options2.quiet ? null : createSpinner({ text: "Resolving dependencies..." });
|
|
12153
11945
|
spin?.start();
|
|
12154
11946
|
try {
|
|
12155
|
-
const resolved = await resolveDependencies(
|
|
11947
|
+
const resolved = await resolveDependencies(config2.registries, componentNames);
|
|
12156
11948
|
spin?.succeed(`Resolved ${resolved.components.length} components`);
|
|
12157
11949
|
if (options2.verbose) {
|
|
12158
11950
|
logger.info("Install order:");
|
|
@@ -12166,17 +11958,19 @@ async function runAdd(componentNames, options2) {
|
|
|
12166
11958
|
logResolved(resolved);
|
|
12167
11959
|
return;
|
|
12168
11960
|
}
|
|
12169
|
-
const
|
|
12170
|
-
|
|
11961
|
+
const fetchSpin = options2.quiet ? null : createSpinner({ text: "Fetching components..." });
|
|
11962
|
+
fetchSpin?.start();
|
|
11963
|
+
const componentBundles = [];
|
|
12171
11964
|
for (const component of resolved.components) {
|
|
12172
11965
|
const files = [];
|
|
12173
11966
|
for (const file of component.files) {
|
|
12174
|
-
const
|
|
12175
|
-
files.push({ path: file.path, content: Buffer.from(
|
|
11967
|
+
const content2 = await fetchFileContent(component.baseUrl, component.name, file.path);
|
|
11968
|
+
files.push({ path: file.path, content: Buffer.from(content2) });
|
|
12176
11969
|
}
|
|
12177
11970
|
const computedHash = await hashBundle(files);
|
|
12178
11971
|
const existingEntry = lock.installed[component.qualifiedName];
|
|
12179
11972
|
if (existingEntry && existingEntry.hash !== computedHash) {
|
|
11973
|
+
fetchSpin?.fail("Integrity check failed");
|
|
12180
11974
|
throw new IntegrityError(component.qualifiedName, existingEntry.hash, computedHash);
|
|
12181
11975
|
}
|
|
12182
11976
|
for (const file of component.files) {
|
|
@@ -12184,6 +11978,7 @@ async function runAdd(componentNames, options2) {
|
|
|
12184
11978
|
if (existsSync(targetPath)) {
|
|
12185
11979
|
const conflictingComponent = findComponentByFile(lock, file.target);
|
|
12186
11980
|
if (conflictingComponent && conflictingComponent !== component.qualifiedName) {
|
|
11981
|
+
fetchSpin?.fail("File conflict detected");
|
|
12187
11982
|
throw new ConflictError(`File conflict: ${file.target} already exists (installed by '${conflictingComponent}').
|
|
12188
11983
|
|
|
12189
11984
|
` + `To resolve:
|
|
@@ -12193,7 +11988,54 @@ async function runAdd(componentNames, options2) {
|
|
|
12193
11988
|
}
|
|
12194
11989
|
}
|
|
12195
11990
|
}
|
|
12196
|
-
|
|
11991
|
+
componentBundles.push({ component, files, computedHash });
|
|
11992
|
+
}
|
|
11993
|
+
fetchSpin?.succeed(`Fetched ${resolved.components.length} components`);
|
|
11994
|
+
const allConflicts = [];
|
|
11995
|
+
for (const { component, files } of componentBundles) {
|
|
11996
|
+
for (const file of files) {
|
|
11997
|
+
const componentFile = component.files.find((f) => f.path === file.path);
|
|
11998
|
+
if (!componentFile)
|
|
11999
|
+
continue;
|
|
12000
|
+
const targetPath = join(cwd, componentFile.target);
|
|
12001
|
+
if (existsSync(targetPath)) {
|
|
12002
|
+
const existingContent = await Bun.file(targetPath).text();
|
|
12003
|
+
const incomingContent = file.content.toString("utf-8");
|
|
12004
|
+
if (!isContentIdentical(existingContent, incomingContent) && !options2.yes) {
|
|
12005
|
+
allConflicts.push(componentFile.target);
|
|
12006
|
+
}
|
|
12007
|
+
}
|
|
12008
|
+
}
|
|
12009
|
+
}
|
|
12010
|
+
if (allConflicts.length > 0) {
|
|
12011
|
+
logger.error("");
|
|
12012
|
+
logger.error("File conflicts detected:");
|
|
12013
|
+
for (const conflict of allConflicts) {
|
|
12014
|
+
logger.error(` \u2717 ${conflict}`);
|
|
12015
|
+
}
|
|
12016
|
+
logger.error("");
|
|
12017
|
+
logger.error("These files have been modified since installation.");
|
|
12018
|
+
logger.error("Use --yes to overwrite, or review the changes first.");
|
|
12019
|
+
throw new ConflictError(`${allConflicts.length} file(s) have conflicts. Use --yes to overwrite.`);
|
|
12020
|
+
}
|
|
12021
|
+
const installSpin = options2.quiet ? null : createSpinner({ text: "Installing components..." });
|
|
12022
|
+
installSpin?.start();
|
|
12023
|
+
for (const { component, files, computedHash } of componentBundles) {
|
|
12024
|
+
const installResult = await installComponent(component, files, cwd, {
|
|
12025
|
+
yes: options2.yes,
|
|
12026
|
+
verbose: options2.verbose
|
|
12027
|
+
});
|
|
12028
|
+
if (options2.verbose) {
|
|
12029
|
+
for (const f of installResult.skipped) {
|
|
12030
|
+
logger.info(` \u25CB Skipped ${f} (unchanged)`);
|
|
12031
|
+
}
|
|
12032
|
+
for (const f of installResult.overwritten) {
|
|
12033
|
+
logger.info(` \u2713 Overwrote ${f}`);
|
|
12034
|
+
}
|
|
12035
|
+
for (const f of installResult.written) {
|
|
12036
|
+
logger.info(` \u2713 Wrote ${f}`);
|
|
12037
|
+
}
|
|
12038
|
+
}
|
|
12197
12039
|
const index = await fetchRegistryIndex(component.baseUrl);
|
|
12198
12040
|
lock.installed[component.qualifiedName] = {
|
|
12199
12041
|
registry: component.registryName,
|
|
@@ -12204,43 +12046,15 @@ async function runAdd(componentNames, options2) {
|
|
|
12204
12046
|
};
|
|
12205
12047
|
}
|
|
12206
12048
|
installSpin?.succeed(`Installed ${resolved.components.length} components`);
|
|
12207
|
-
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
|
|
12212
|
-
|
|
12213
|
-
|
|
12214
|
-
mcpServers: resolved.mcpServers,
|
|
12215
|
-
agentMcpBindings: resolved.agentMcpBindings,
|
|
12216
|
-
disabledTools: resolved.disabledTools,
|
|
12217
|
-
plugins: resolved.plugins,
|
|
12218
|
-
agentConfigs: resolved.agentConfigs,
|
|
12219
|
-
instructions: resolved.instructions
|
|
12220
|
-
});
|
|
12221
|
-
if (result.mcpSkipped.length > 0 && !options2.quiet) {
|
|
12222
|
-
for (const name of result.mcpSkipped) {
|
|
12223
|
-
logger.warn(`MCP server "${name}" already configured, skipped`);
|
|
12224
|
-
}
|
|
12225
|
-
}
|
|
12226
|
-
if (!options2.quiet && result.mcpAdded.length > 0) {
|
|
12227
|
-
logger.info(`Configured ${result.mcpAdded.length} MCP servers`);
|
|
12228
|
-
for (const binding of resolved.agentMcpBindings) {
|
|
12229
|
-
logger.info(` Scoped to agent "${binding.agentName}": ${binding.serverNames.join(", ")}`);
|
|
12049
|
+
if (resolved.opencode && Object.keys(resolved.opencode).length > 0) {
|
|
12050
|
+
const result = await updateOpencodeJsonConfig(cwd, resolved.opencode);
|
|
12051
|
+
if (!options2.quiet && result.changed) {
|
|
12052
|
+
if (result.created) {
|
|
12053
|
+
logger.info("Created opencode.jsonc");
|
|
12054
|
+
} else {
|
|
12055
|
+
logger.info("Updated opencode.jsonc");
|
|
12230
12056
|
}
|
|
12231
12057
|
}
|
|
12232
|
-
if (!options2.quiet && result.toolsDisabled.length > 0) {
|
|
12233
|
-
logger.info(`Disabled ${result.toolsDisabled.length} tools: ${result.toolsDisabled.join(", ")}`);
|
|
12234
|
-
}
|
|
12235
|
-
if (!options2.quiet && result.pluginsAdded.length > 0) {
|
|
12236
|
-
logger.info(`Added ${result.pluginsAdded.length} plugins: ${result.pluginsAdded.join(", ")}`);
|
|
12237
|
-
}
|
|
12238
|
-
if (!options2.quiet && result.agentsConfigured.length > 0) {
|
|
12239
|
-
logger.info(`Configured ${result.agentsConfigured.length} agents: ${result.agentsConfigured.join(", ")}`);
|
|
12240
|
-
}
|
|
12241
|
-
if (!options2.quiet && result.instructionsAdded.length > 0) {
|
|
12242
|
-
logger.info(`Added ${result.instructionsAdded.length} instructions`);
|
|
12243
|
-
}
|
|
12244
12058
|
}
|
|
12245
12059
|
const hasNpmDeps = resolved.npmDependencies.length > 0;
|
|
12246
12060
|
const hasNpmDevDeps = resolved.npmDevDependencies.length > 0;
|
|
@@ -12261,32 +12075,51 @@ async function runAdd(componentNames, options2) {
|
|
|
12261
12075
|
console.log(JSON.stringify({
|
|
12262
12076
|
success: true,
|
|
12263
12077
|
installed: resolved.installOrder,
|
|
12264
|
-
|
|
12078
|
+
opencode: !!resolved.opencode
|
|
12265
12079
|
}, null, 2));
|
|
12266
12080
|
} else if (!options2.quiet) {
|
|
12267
12081
|
logger.info("");
|
|
12268
12082
|
logger.success(`Done! Installed ${resolved.components.length} components.`);
|
|
12269
12083
|
}
|
|
12270
12084
|
} catch (error) {
|
|
12271
|
-
spin
|
|
12085
|
+
if (spin && !spin.isSpinning) {} else {
|
|
12086
|
+
spin?.fail("Failed to add components");
|
|
12087
|
+
}
|
|
12272
12088
|
throw error;
|
|
12273
12089
|
}
|
|
12274
12090
|
}
|
|
12275
|
-
async function installComponent(component, files, cwd) {
|
|
12091
|
+
async function installComponent(component, files, cwd, _options) {
|
|
12092
|
+
const result = {
|
|
12093
|
+
written: [],
|
|
12094
|
+
skipped: [],
|
|
12095
|
+
overwritten: []
|
|
12096
|
+
};
|
|
12276
12097
|
for (const file of files) {
|
|
12277
12098
|
const componentFile = component.files.find((f) => f.path === file.path);
|
|
12278
12099
|
if (!componentFile)
|
|
12279
12100
|
continue;
|
|
12280
12101
|
const targetPath = join(cwd, componentFile.target);
|
|
12281
12102
|
const targetDir = dirname(targetPath);
|
|
12103
|
+
if (existsSync(targetPath)) {
|
|
12104
|
+
const existingContent = await Bun.file(targetPath).text();
|
|
12105
|
+
const incomingContent = file.content.toString("utf-8");
|
|
12106
|
+
if (isContentIdentical(existingContent, incomingContent)) {
|
|
12107
|
+
result.skipped.push(componentFile.target);
|
|
12108
|
+
continue;
|
|
12109
|
+
}
|
|
12110
|
+
result.overwritten.push(componentFile.target);
|
|
12111
|
+
} else {
|
|
12112
|
+
result.written.push(componentFile.target);
|
|
12113
|
+
}
|
|
12282
12114
|
if (!existsSync(targetDir)) {
|
|
12283
12115
|
await mkdir(targetDir, { recursive: true });
|
|
12284
12116
|
}
|
|
12285
12117
|
await writeFile(targetPath, file.content);
|
|
12286
12118
|
}
|
|
12119
|
+
return result;
|
|
12287
12120
|
}
|
|
12288
|
-
async function hashContent(
|
|
12289
|
-
return createHash("sha256").update(
|
|
12121
|
+
async function hashContent(content2) {
|
|
12122
|
+
return createHash("sha256").update(content2).digest("hex");
|
|
12290
12123
|
}
|
|
12291
12124
|
async function hashBundle(files) {
|
|
12292
12125
|
const sorted = [...files].sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -12304,11 +12137,11 @@ function logResolved(resolved) {
|
|
|
12304
12137
|
for (const component of resolved.components) {
|
|
12305
12138
|
logger.info(` ${component.name} (${component.type}) from ${component.registryName}`);
|
|
12306
12139
|
}
|
|
12307
|
-
if (Object.keys(resolved.
|
|
12140
|
+
if (resolved.opencode && Object.keys(resolved.opencode).length > 0) {
|
|
12308
12141
|
logger.info("");
|
|
12309
|
-
logger.info("Would
|
|
12310
|
-
for (const
|
|
12311
|
-
logger.info(` ${
|
|
12142
|
+
logger.info("Would update opencode.json with:");
|
|
12143
|
+
for (const key of Object.keys(resolved.opencode)) {
|
|
12144
|
+
logger.info(` ${key}`);
|
|
12312
12145
|
}
|
|
12313
12146
|
}
|
|
12314
12147
|
if (resolved.npmDependencies.length > 0) {
|
|
@@ -12360,8 +12193,8 @@ async function readOpencodePackageJson(opencodeDir) {
|
|
|
12360
12193
|
return { ...DEFAULT_PACKAGE_JSON };
|
|
12361
12194
|
}
|
|
12362
12195
|
try {
|
|
12363
|
-
const
|
|
12364
|
-
return JSON.parse(
|
|
12196
|
+
const content2 = await Bun.file(pkgPath).text();
|
|
12197
|
+
return JSON.parse(content2);
|
|
12365
12198
|
} catch (e3) {
|
|
12366
12199
|
const message = e3 instanceof Error ? e3.message : String(e3);
|
|
12367
12200
|
throw new ConfigError(`Invalid .opencode/package.json: ${message}`);
|
|
@@ -12373,8 +12206,8 @@ async function ensureManifestFilesAreTracked(opencodeDir) {
|
|
|
12373
12206
|
const requiredIgnores = ["node_modules"];
|
|
12374
12207
|
let lines = [];
|
|
12375
12208
|
if (existsSync(gitignorePath)) {
|
|
12376
|
-
const
|
|
12377
|
-
lines =
|
|
12209
|
+
const content2 = await Bun.file(gitignorePath).text();
|
|
12210
|
+
lines = content2.split(`
|
|
12378
12211
|
`).map((l) => l.trim()).filter(Boolean);
|
|
12379
12212
|
}
|
|
12380
12213
|
lines = lines.filter((line) => !filesToTrack.has(line));
|
|
@@ -12441,18 +12274,18 @@ function registerBuildCommand(program2) {
|
|
|
12441
12274
|
}
|
|
12442
12275
|
process.exit(1);
|
|
12443
12276
|
}
|
|
12444
|
-
const
|
|
12277
|
+
const registry2 = parseResult.data;
|
|
12445
12278
|
const validationErrors = [];
|
|
12446
12279
|
const componentsDir = join2(outPath, "components");
|
|
12447
12280
|
await mkdir2(componentsDir, { recursive: true });
|
|
12448
|
-
for (const component of
|
|
12281
|
+
for (const component of registry2.components) {
|
|
12449
12282
|
const packument = {
|
|
12450
12283
|
name: component.name,
|
|
12451
12284
|
versions: {
|
|
12452
|
-
[
|
|
12285
|
+
[registry2.version]: component
|
|
12453
12286
|
},
|
|
12454
12287
|
"dist-tags": {
|
|
12455
|
-
latest:
|
|
12288
|
+
latest: registry2.version
|
|
12456
12289
|
}
|
|
12457
12290
|
};
|
|
12458
12291
|
const packumentPath = join2(componentsDir, `${component.name}.json`);
|
|
@@ -12481,11 +12314,11 @@ function registerBuildCommand(program2) {
|
|
|
12481
12314
|
process.exit(1);
|
|
12482
12315
|
}
|
|
12483
12316
|
const index = {
|
|
12484
|
-
name:
|
|
12485
|
-
namespace:
|
|
12486
|
-
version:
|
|
12487
|
-
author:
|
|
12488
|
-
components:
|
|
12317
|
+
name: registry2.name,
|
|
12318
|
+
namespace: registry2.namespace,
|
|
12319
|
+
version: registry2.version,
|
|
12320
|
+
author: registry2.author,
|
|
12321
|
+
components: registry2.components.map((c) => ({
|
|
12489
12322
|
name: c.name,
|
|
12490
12323
|
type: c.type,
|
|
12491
12324
|
description: c.description
|
|
@@ -12493,19 +12326,19 @@ function registerBuildCommand(program2) {
|
|
|
12493
12326
|
};
|
|
12494
12327
|
await Bun.write(join2(outPath, "index.json"), JSON.stringify(index, null, 2));
|
|
12495
12328
|
if (!options2.json) {
|
|
12496
|
-
const msg = `Built ${
|
|
12329
|
+
const msg = `Built ${registry2.components.length} components to ${relative(options2.cwd, outPath)}`;
|
|
12497
12330
|
spinner2.succeed(msg);
|
|
12498
12331
|
if (!process.stdout.isTTY) {
|
|
12499
|
-
logger.success(`Built ${
|
|
12332
|
+
logger.success(`Built ${registry2.components.length} components`);
|
|
12500
12333
|
}
|
|
12501
12334
|
}
|
|
12502
12335
|
if (options2.json) {
|
|
12503
12336
|
outputJson({
|
|
12504
12337
|
success: true,
|
|
12505
12338
|
data: {
|
|
12506
|
-
name:
|
|
12507
|
-
version:
|
|
12508
|
-
components:
|
|
12339
|
+
name: registry2.name,
|
|
12340
|
+
version: registry2.version,
|
|
12341
|
+
components: registry2.components.length,
|
|
12509
12342
|
output: outPath
|
|
12510
12343
|
}
|
|
12511
12344
|
});
|
|
@@ -13320,8 +13153,8 @@ function registerDiffCommand(program2) {
|
|
|
13320
13153
|
}
|
|
13321
13154
|
return;
|
|
13322
13155
|
}
|
|
13323
|
-
const
|
|
13324
|
-
if (!
|
|
13156
|
+
const config2 = await readOcxConfig(options2.cwd);
|
|
13157
|
+
if (!config2) {
|
|
13325
13158
|
if (options2.json) {
|
|
13326
13159
|
outputJson({
|
|
13327
13160
|
success: false,
|
|
@@ -13361,7 +13194,7 @@ function registerDiffCommand(program2) {
|
|
|
13361
13194
|
continue;
|
|
13362
13195
|
}
|
|
13363
13196
|
const localContent = await localFile.text();
|
|
13364
|
-
const registryConfig =
|
|
13197
|
+
const registryConfig = config2.registries[installed.registry];
|
|
13365
13198
|
if (!registryConfig) {
|
|
13366
13199
|
logger.warn(`Registry '${installed.registry}' not configured for component '${name}'.`);
|
|
13367
13200
|
continue;
|
|
@@ -13413,12 +13246,7 @@ import { join as join3 } from "path";
|
|
|
13413
13246
|
|
|
13414
13247
|
// src/constants.ts
|
|
13415
13248
|
var OCX_DOMAIN = "ocx.kdco.dev";
|
|
13416
|
-
var GITHUB_REPO = "kdcokenny/ocx";
|
|
13417
13249
|
var OCX_SCHEMA_URL = `https://${OCX_DOMAIN}/schema.json`;
|
|
13418
|
-
var OCX_LOCK_SCHEMA_URL = `https://${OCX_DOMAIN}/lock.schema.json`;
|
|
13419
|
-
var OCX_INSTALL_URL = `https://${OCX_DOMAIN}/install.sh`;
|
|
13420
|
-
var GITHUB_RELEASES_URL = `https://github.com/${GITHUB_REPO}/releases`;
|
|
13421
|
-
var GITHUB_RAW_URL = `https://raw.githubusercontent.com/${GITHUB_REPO}/main`;
|
|
13422
13250
|
|
|
13423
13251
|
// src/commands/init.ts
|
|
13424
13252
|
function registerInitCommand(program2) {
|
|
@@ -13448,9 +13276,9 @@ async function runInit(options2) {
|
|
|
13448
13276
|
$schema: OCX_SCHEMA_URL,
|
|
13449
13277
|
registries: {}
|
|
13450
13278
|
};
|
|
13451
|
-
const
|
|
13452
|
-
const
|
|
13453
|
-
await writeFile2(configPath,
|
|
13279
|
+
const config2 = ocxConfigSchema.parse(rawConfig);
|
|
13280
|
+
const content2 = JSON.stringify(config2, null, 2);
|
|
13281
|
+
await writeFile2(configPath, content2, "utf-8");
|
|
13454
13282
|
if (!options2.quiet && !options2.json) {
|
|
13455
13283
|
logger.success("Initialized OCX configuration");
|
|
13456
13284
|
}
|
|
@@ -13472,28 +13300,28 @@ async function runInit(options2) {
|
|
|
13472
13300
|
|
|
13473
13301
|
// src/commands/registry.ts
|
|
13474
13302
|
function registerRegistryCommand(program2) {
|
|
13475
|
-
const
|
|
13476
|
-
|
|
13303
|
+
const registry2 = program2.command("registry").description("Manage registries");
|
|
13304
|
+
registry2.command("add").description("Add a registry").argument("<url>", "Registry URL").option("--name <name>", "Registry alias (defaults to hostname)").option("--version <version>", "Pin to specific version").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output as JSON", false).option("-q, --quiet", "Suppress output", false).action(async (url, options2) => {
|
|
13477
13305
|
try {
|
|
13478
|
-
const
|
|
13479
|
-
if (!
|
|
13306
|
+
const config2 = await readOcxConfig(options2.cwd);
|
|
13307
|
+
if (!config2) {
|
|
13480
13308
|
logger.error("No ocx.jsonc found. Run 'ocx init' first.");
|
|
13481
13309
|
process.exit(1);
|
|
13482
13310
|
}
|
|
13483
|
-
if (
|
|
13311
|
+
if (config2.lockRegistries) {
|
|
13484
13312
|
logger.error("Registries are locked. Cannot add.");
|
|
13485
13313
|
process.exit(1);
|
|
13486
13314
|
}
|
|
13487
13315
|
const name = options2.name || new URL(url).hostname.replace(/\./g, "-");
|
|
13488
|
-
if (
|
|
13316
|
+
if (config2.registries[name]) {
|
|
13489
13317
|
logger.warn(`Registry '${name}' already exists. Use a different name.`);
|
|
13490
13318
|
return;
|
|
13491
13319
|
}
|
|
13492
|
-
|
|
13320
|
+
config2.registries[name] = {
|
|
13493
13321
|
url,
|
|
13494
13322
|
version: options2.version
|
|
13495
13323
|
};
|
|
13496
|
-
await writeOcxConfig(options2.cwd,
|
|
13324
|
+
await writeOcxConfig(options2.cwd, config2);
|
|
13497
13325
|
if (options2.json) {
|
|
13498
13326
|
outputJson({ success: true, data: { name, url } });
|
|
13499
13327
|
} else {
|
|
@@ -13503,23 +13331,23 @@ function registerRegistryCommand(program2) {
|
|
|
13503
13331
|
handleError(error);
|
|
13504
13332
|
}
|
|
13505
13333
|
});
|
|
13506
|
-
|
|
13334
|
+
registry2.command("remove").description("Remove a registry").argument("<name>", "Registry name").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output as JSON", false).option("-q, --quiet", "Suppress output", false).action(async (name, options2) => {
|
|
13507
13335
|
try {
|
|
13508
|
-
const
|
|
13509
|
-
if (!
|
|
13336
|
+
const config2 = await readOcxConfig(options2.cwd);
|
|
13337
|
+
if (!config2) {
|
|
13510
13338
|
logger.error("No ocx.jsonc found. Run 'ocx init' first.");
|
|
13511
13339
|
process.exit(1);
|
|
13512
13340
|
}
|
|
13513
|
-
if (
|
|
13341
|
+
if (config2.lockRegistries) {
|
|
13514
13342
|
logger.error("Registries are locked. Cannot remove.");
|
|
13515
13343
|
process.exit(1);
|
|
13516
13344
|
}
|
|
13517
|
-
if (!
|
|
13345
|
+
if (!config2.registries[name]) {
|
|
13518
13346
|
logger.warn(`Registry '${name}' not found.`);
|
|
13519
13347
|
return;
|
|
13520
13348
|
}
|
|
13521
|
-
delete
|
|
13522
|
-
await writeOcxConfig(options2.cwd,
|
|
13349
|
+
delete config2.registries[name];
|
|
13350
|
+
await writeOcxConfig(options2.cwd, config2);
|
|
13523
13351
|
if (options2.json) {
|
|
13524
13352
|
outputJson({ success: true, data: { removed: name } });
|
|
13525
13353
|
} else {
|
|
@@ -13529,14 +13357,14 @@ function registerRegistryCommand(program2) {
|
|
|
13529
13357
|
handleError(error);
|
|
13530
13358
|
}
|
|
13531
13359
|
});
|
|
13532
|
-
|
|
13360
|
+
registry2.command("list").description("List configured registries").option("--cwd <path>", "Working directory", process.cwd()).option("--json", "Output as JSON", false).option("-q, --quiet", "Suppress output", false).action(async (options2) => {
|
|
13533
13361
|
try {
|
|
13534
|
-
const
|
|
13535
|
-
if (!
|
|
13362
|
+
const config2 = await readOcxConfig(options2.cwd);
|
|
13363
|
+
if (!config2) {
|
|
13536
13364
|
logger.warn("No ocx.jsonc found. Run 'ocx init' first.");
|
|
13537
13365
|
return;
|
|
13538
13366
|
}
|
|
13539
|
-
const registries = Object.entries(
|
|
13367
|
+
const registries = Object.entries(config2.registries).map(([name, cfg]) => ({
|
|
13540
13368
|
name,
|
|
13541
13369
|
url: cfg.url,
|
|
13542
13370
|
version: cfg.version || "latest"
|
|
@@ -13546,14 +13374,14 @@ function registerRegistryCommand(program2) {
|
|
|
13546
13374
|
success: true,
|
|
13547
13375
|
data: {
|
|
13548
13376
|
registries,
|
|
13549
|
-
locked:
|
|
13377
|
+
locked: config2.lockRegistries
|
|
13550
13378
|
}
|
|
13551
13379
|
});
|
|
13552
13380
|
} else {
|
|
13553
13381
|
if (registries.length === 0) {
|
|
13554
13382
|
logger.info("No registries configured.");
|
|
13555
13383
|
} else {
|
|
13556
|
-
logger.info(`Configured registries${
|
|
13384
|
+
logger.info(`Configured registries${config2.lockRegistries ? kleur_default.yellow(" (locked)") : ""}:`);
|
|
13557
13385
|
for (const reg of registries) {
|
|
13558
13386
|
console.log(` ${kleur_default.cyan(reg.name)}: ${reg.url} ${kleur_default.dim(`(${reg.version})`)}`);
|
|
13559
13387
|
}
|
|
@@ -13597,13 +13425,13 @@ function registerSearchCommand(program2) {
|
|
|
13597
13425
|
}
|
|
13598
13426
|
return;
|
|
13599
13427
|
}
|
|
13600
|
-
const
|
|
13601
|
-
if (!
|
|
13428
|
+
const config2 = await readOcxConfig(options2.cwd);
|
|
13429
|
+
if (!config2) {
|
|
13602
13430
|
logger.warn("No ocx.jsonc found. Run 'ocx init' first.");
|
|
13603
13431
|
return;
|
|
13604
13432
|
}
|
|
13605
13433
|
if (options2.verbose) {
|
|
13606
|
-
logger.info(`Searching in ${Object.keys(
|
|
13434
|
+
logger.info(`Searching in ${Object.keys(config2.registries).length} registries...`);
|
|
13607
13435
|
}
|
|
13608
13436
|
const allComponents = [];
|
|
13609
13437
|
const spinner2 = createSpinner({
|
|
@@ -13613,7 +13441,7 @@ function registerSearchCommand(program2) {
|
|
|
13613
13441
|
if (!options2.json && !options2.verbose) {
|
|
13614
13442
|
spinner2.start();
|
|
13615
13443
|
}
|
|
13616
|
-
for (const [registryName, registryConfig] of Object.entries(
|
|
13444
|
+
for (const [registryName, registryConfig] of Object.entries(config2.registries)) {
|
|
13617
13445
|
try {
|
|
13618
13446
|
if (options2.verbose) {
|
|
13619
13447
|
logger.info(`Fetching index from ${registryName} (${registryConfig.url})...`);
|
|
@@ -13668,7 +13496,7 @@ function registerSearchCommand(program2) {
|
|
|
13668
13496
|
}
|
|
13669
13497
|
|
|
13670
13498
|
// src/index.ts
|
|
13671
|
-
var version = "1.0.
|
|
13499
|
+
var version = "1.0.11";
|
|
13672
13500
|
async function main2() {
|
|
13673
13501
|
const program2 = new Command().name("ocx").description("OpenCode Extensions - Install agents, skills, plugins, and commands").version(version);
|
|
13674
13502
|
registerInitCommand(program2);
|
|
@@ -13683,4 +13511,4 @@ main2().catch((err) => {
|
|
|
13683
13511
|
handleError(err);
|
|
13684
13512
|
});
|
|
13685
13513
|
|
|
13686
|
-
//# debugId=
|
|
13514
|
+
//# debugId=7C617E82FAE81D9864756E2164756E21
|