@openchamber/web 1.7.0 → 1.7.1
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/assets/{ToolOutputDialog-B0blBQQ2.js → ToolOutputDialog-BltFD_Q9.js} +1 -1
- package/dist/assets/index-9lSEGPDM.css +1 -0
- package/dist/assets/{index-hNdAvOl1.js → index-CHfHzhF4.js} +2 -2
- package/dist/assets/main-BV1LQs35.js +294 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/index.js +195 -22
- package/server/lib/opencode-config.js +258 -68
- package/server/lib/skills-catalog/clawdhub/install.js +20 -5
- package/server/lib/skills-catalog/install.js +19 -5
- package/dist/assets/index-DWAk1auj.css +0 -1
- package/dist/assets/main-K9J17_P4.js +0 -288
package/dist/index.html
CHANGED
|
@@ -203,10 +203,10 @@
|
|
|
203
203
|
pointer-events: none;
|
|
204
204
|
}
|
|
205
205
|
</style>
|
|
206
|
-
<script type="module" crossorigin src="/assets/index-
|
|
206
|
+
<script type="module" crossorigin src="/assets/index-CHfHzhF4.js"></script>
|
|
207
207
|
<link rel="modulepreload" crossorigin href="/assets/vendor-.bun-Dd1DA83-.js">
|
|
208
208
|
<link rel="stylesheet" crossorigin href="/assets/vendor--DbVqbJpV.css">
|
|
209
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
209
|
+
<link rel="stylesheet" crossorigin href="/assets/index-9lSEGPDM.css">
|
|
210
210
|
</head>
|
|
211
211
|
<body class="h-full bg-background text-foreground">
|
|
212
212
|
<div id="root" class="h-full">
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -6893,6 +6893,133 @@ async function main(options = {}) {
|
|
|
6893
6893
|
SKILL_DIR,
|
|
6894
6894
|
} = await import('./lib/opencode-config.js');
|
|
6895
6895
|
|
|
6896
|
+
const findWorktreeRootForSkills = (workingDirectory) => {
|
|
6897
|
+
if (!workingDirectory) return null;
|
|
6898
|
+
let current = path.resolve(workingDirectory);
|
|
6899
|
+
while (true) {
|
|
6900
|
+
if (fs.existsSync(path.join(current, '.git'))) {
|
|
6901
|
+
return current;
|
|
6902
|
+
}
|
|
6903
|
+
const parent = path.dirname(current);
|
|
6904
|
+
if (parent === current) {
|
|
6905
|
+
return null;
|
|
6906
|
+
}
|
|
6907
|
+
current = parent;
|
|
6908
|
+
}
|
|
6909
|
+
};
|
|
6910
|
+
|
|
6911
|
+
const getSkillProjectAncestors = (workingDirectory) => {
|
|
6912
|
+
if (!workingDirectory) return [];
|
|
6913
|
+
const result = [];
|
|
6914
|
+
let current = path.resolve(workingDirectory);
|
|
6915
|
+
const stop = findWorktreeRootForSkills(workingDirectory) || current;
|
|
6916
|
+
while (true) {
|
|
6917
|
+
result.push(current);
|
|
6918
|
+
if (current === stop) break;
|
|
6919
|
+
const parent = path.dirname(current);
|
|
6920
|
+
if (parent === current) break;
|
|
6921
|
+
current = parent;
|
|
6922
|
+
}
|
|
6923
|
+
return result;
|
|
6924
|
+
};
|
|
6925
|
+
|
|
6926
|
+
const isPathInside = (candidatePath, parentPath) => {
|
|
6927
|
+
if (!candidatePath || !parentPath) return false;
|
|
6928
|
+
const normalizedCandidate = path.resolve(candidatePath);
|
|
6929
|
+
const normalizedParent = path.resolve(parentPath);
|
|
6930
|
+
return normalizedCandidate === normalizedParent || normalizedCandidate.startsWith(`${normalizedParent}${path.sep}`);
|
|
6931
|
+
};
|
|
6932
|
+
|
|
6933
|
+
const inferSkillScopeAndSourceFromPath = (skillPath, workingDirectory) => {
|
|
6934
|
+
const resolvedPath = typeof skillPath === 'string' ? path.resolve(skillPath) : '';
|
|
6935
|
+
const home = os.homedir();
|
|
6936
|
+
const source = resolvedPath.includes(`${path.sep}.agents${path.sep}skills${path.sep}`)
|
|
6937
|
+
? 'agents'
|
|
6938
|
+
: resolvedPath.includes(`${path.sep}.claude${path.sep}skills${path.sep}`)
|
|
6939
|
+
? 'claude'
|
|
6940
|
+
: 'opencode';
|
|
6941
|
+
|
|
6942
|
+
const projectAncestors = getSkillProjectAncestors(workingDirectory);
|
|
6943
|
+
const isProjectScoped = projectAncestors.some((ancestor) => {
|
|
6944
|
+
const candidates = [
|
|
6945
|
+
path.join(ancestor, '.opencode'),
|
|
6946
|
+
path.join(ancestor, '.claude', 'skills'),
|
|
6947
|
+
path.join(ancestor, '.agents', 'skills'),
|
|
6948
|
+
];
|
|
6949
|
+
return candidates.some((candidate) => isPathInside(resolvedPath, candidate));
|
|
6950
|
+
});
|
|
6951
|
+
|
|
6952
|
+
if (isProjectScoped) {
|
|
6953
|
+
return { scope: SKILL_SCOPE.PROJECT, source };
|
|
6954
|
+
}
|
|
6955
|
+
|
|
6956
|
+
const userRoots = [
|
|
6957
|
+
path.join(home, '.config', 'opencode'),
|
|
6958
|
+
path.join(home, '.opencode'),
|
|
6959
|
+
path.join(home, '.claude', 'skills'),
|
|
6960
|
+
path.join(home, '.agents', 'skills'),
|
|
6961
|
+
process.env.OPENCODE_CONFIG_DIR ? path.resolve(process.env.OPENCODE_CONFIG_DIR) : null,
|
|
6962
|
+
].filter(Boolean);
|
|
6963
|
+
|
|
6964
|
+
if (userRoots.some((root) => isPathInside(resolvedPath, root))) {
|
|
6965
|
+
return { scope: SKILL_SCOPE.USER, source };
|
|
6966
|
+
}
|
|
6967
|
+
|
|
6968
|
+
return { scope: SKILL_SCOPE.USER, source };
|
|
6969
|
+
};
|
|
6970
|
+
|
|
6971
|
+
const fetchOpenCodeDiscoveredSkills = async (workingDirectory) => {
|
|
6972
|
+
if (!openCodePort) {
|
|
6973
|
+
return null;
|
|
6974
|
+
}
|
|
6975
|
+
|
|
6976
|
+
try {
|
|
6977
|
+
const url = new URL(buildOpenCodeUrl('/skill', ''));
|
|
6978
|
+
if (workingDirectory) {
|
|
6979
|
+
url.searchParams.set('directory', workingDirectory);
|
|
6980
|
+
}
|
|
6981
|
+
|
|
6982
|
+
const response = await fetch(url.toString(), {
|
|
6983
|
+
method: 'GET',
|
|
6984
|
+
headers: {
|
|
6985
|
+
Accept: 'application/json',
|
|
6986
|
+
...getOpenCodeAuthHeaders(),
|
|
6987
|
+
},
|
|
6988
|
+
signal: AbortSignal.timeout(8_000),
|
|
6989
|
+
});
|
|
6990
|
+
|
|
6991
|
+
if (!response.ok) {
|
|
6992
|
+
return null;
|
|
6993
|
+
}
|
|
6994
|
+
|
|
6995
|
+
const payload = await response.json();
|
|
6996
|
+
if (!Array.isArray(payload)) {
|
|
6997
|
+
return null;
|
|
6998
|
+
}
|
|
6999
|
+
|
|
7000
|
+
return payload
|
|
7001
|
+
.map((item) => {
|
|
7002
|
+
const name = typeof item?.name === 'string' ? item.name.trim() : '';
|
|
7003
|
+
const location = typeof item?.location === 'string' ? item.location : '';
|
|
7004
|
+
const description = typeof item?.description === 'string' ? item.description : '';
|
|
7005
|
+
if (!name || !location) {
|
|
7006
|
+
return null;
|
|
7007
|
+
}
|
|
7008
|
+
const inferred = inferSkillScopeAndSourceFromPath(location, workingDirectory);
|
|
7009
|
+
return {
|
|
7010
|
+
name,
|
|
7011
|
+
path: location,
|
|
7012
|
+
scope: inferred.scope,
|
|
7013
|
+
source: inferred.source,
|
|
7014
|
+
description,
|
|
7015
|
+
};
|
|
7016
|
+
})
|
|
7017
|
+
.filter(Boolean);
|
|
7018
|
+
} catch {
|
|
7019
|
+
return null;
|
|
7020
|
+
}
|
|
7021
|
+
};
|
|
7022
|
+
|
|
6896
7023
|
// List all discovered skills
|
|
6897
7024
|
app.get('/api/config/skills', async (req, res) => {
|
|
6898
7025
|
try {
|
|
@@ -6900,11 +7027,11 @@ async function main(options = {}) {
|
|
|
6900
7027
|
if (!directory) {
|
|
6901
7028
|
return res.status(400).json({ error });
|
|
6902
7029
|
}
|
|
6903
|
-
const skills = discoverSkills(directory);
|
|
7030
|
+
const skills = (await fetchOpenCodeDiscoveredSkills(directory)) || discoverSkills(directory);
|
|
6904
7031
|
|
|
6905
7032
|
// Enrich with full sources info
|
|
6906
7033
|
const enrichedSkills = skills.map(skill => {
|
|
6907
|
-
const sources = getSkillSources(skill.name, directory);
|
|
7034
|
+
const sources = getSkillSources(skill.name, directory, skill);
|
|
6908
7035
|
return {
|
|
6909
7036
|
...skill,
|
|
6910
7037
|
sources
|
|
@@ -7018,7 +7145,9 @@ async function main(options = {}) {
|
|
|
7018
7145
|
return res.status(404).json({ ok: false, error: { kind: 'invalidSource', message: 'Unknown source' } });
|
|
7019
7146
|
}
|
|
7020
7147
|
|
|
7021
|
-
const discovered = directory
|
|
7148
|
+
const discovered = directory
|
|
7149
|
+
? ((await fetchOpenCodeDiscoveredSkills(directory)) || discoverSkills(directory))
|
|
7150
|
+
: [];
|
|
7022
7151
|
const installedByName = new Map(discovered.map((s) => [s.name, s]));
|
|
7023
7152
|
|
|
7024
7153
|
if (src.sourceType === 'clawdhub' || isClawdHubSource(src.source)) {
|
|
@@ -7033,7 +7162,7 @@ async function main(options = {}) {
|
|
|
7033
7162
|
...item,
|
|
7034
7163
|
sourceId: src.id,
|
|
7035
7164
|
installed: installed
|
|
7036
|
-
? { isInstalled: true, scope: installed.scope }
|
|
7165
|
+
? { isInstalled: true, scope: installed.scope, source: installed.source }
|
|
7037
7166
|
: { isInstalled: false },
|
|
7038
7167
|
};
|
|
7039
7168
|
});
|
|
@@ -7077,7 +7206,7 @@ async function main(options = {}) {
|
|
|
7077
7206
|
...item,
|
|
7078
7207
|
gitIdentityId: src.gitIdentityId,
|
|
7079
7208
|
installed: installed
|
|
7080
|
-
? { isInstalled: true, scope: installed.scope }
|
|
7209
|
+
? { isInstalled: true, scope: installed.scope, source: installed.source }
|
|
7081
7210
|
: { isInstalled: false },
|
|
7082
7211
|
};
|
|
7083
7212
|
});
|
|
@@ -7131,6 +7260,7 @@ async function main(options = {}) {
|
|
|
7131
7260
|
subpath,
|
|
7132
7261
|
gitIdentityId,
|
|
7133
7262
|
scope,
|
|
7263
|
+
targetSource,
|
|
7134
7264
|
selections,
|
|
7135
7265
|
conflictPolicy,
|
|
7136
7266
|
conflictDecisions,
|
|
@@ -7152,6 +7282,7 @@ async function main(options = {}) {
|
|
|
7152
7282
|
if (isClawdHubSource(source)) {
|
|
7153
7283
|
const result = await installSkillsFromClawdHub({
|
|
7154
7284
|
scope,
|
|
7285
|
+
targetSource,
|
|
7155
7286
|
workingDirectory,
|
|
7156
7287
|
userSkillDir: SKILL_DIR,
|
|
7157
7288
|
selections,
|
|
@@ -7166,7 +7297,22 @@ async function main(options = {}) {
|
|
|
7166
7297
|
return res.status(400).json({ ok: false, error: result.error });
|
|
7167
7298
|
}
|
|
7168
7299
|
|
|
7169
|
-
|
|
7300
|
+
const installed = result.installed || [];
|
|
7301
|
+
const skipped = result.skipped || [];
|
|
7302
|
+
const requiresReload = installed.length > 0;
|
|
7303
|
+
|
|
7304
|
+
if (requiresReload) {
|
|
7305
|
+
await refreshOpenCodeAfterConfigChange('skills install');
|
|
7306
|
+
}
|
|
7307
|
+
|
|
7308
|
+
return res.json({
|
|
7309
|
+
ok: true,
|
|
7310
|
+
installed,
|
|
7311
|
+
skipped,
|
|
7312
|
+
requiresReload,
|
|
7313
|
+
message: requiresReload ? 'Skills installed successfully. Reloading interface…' : 'No skills were installed',
|
|
7314
|
+
reloadDelayMs: requiresReload ? CLIENT_RELOAD_DELAY_MS : undefined,
|
|
7315
|
+
});
|
|
7170
7316
|
}
|
|
7171
7317
|
|
|
7172
7318
|
// Handle GitHub sources (git clone based)
|
|
@@ -7177,6 +7323,7 @@ async function main(options = {}) {
|
|
|
7177
7323
|
subpath,
|
|
7178
7324
|
identity,
|
|
7179
7325
|
scope,
|
|
7326
|
+
targetSource,
|
|
7180
7327
|
workingDirectory,
|
|
7181
7328
|
userSkillDir: SKILL_DIR,
|
|
7182
7329
|
selections,
|
|
@@ -7202,7 +7349,22 @@ async function main(options = {}) {
|
|
|
7202
7349
|
return res.status(400).json({ ok: false, error: result.error });
|
|
7203
7350
|
}
|
|
7204
7351
|
|
|
7205
|
-
|
|
7352
|
+
const installed = result.installed || [];
|
|
7353
|
+
const skipped = result.skipped || [];
|
|
7354
|
+
const requiresReload = installed.length > 0;
|
|
7355
|
+
|
|
7356
|
+
if (requiresReload) {
|
|
7357
|
+
await refreshOpenCodeAfterConfigChange('skills install');
|
|
7358
|
+
}
|
|
7359
|
+
|
|
7360
|
+
res.json({
|
|
7361
|
+
ok: true,
|
|
7362
|
+
installed,
|
|
7363
|
+
skipped,
|
|
7364
|
+
requiresReload,
|
|
7365
|
+
message: requiresReload ? 'Skills installed successfully. Reloading interface…' : 'No skills were installed',
|
|
7366
|
+
reloadDelayMs: requiresReload ? CLIENT_RELOAD_DELAY_MS : undefined,
|
|
7367
|
+
});
|
|
7206
7368
|
} catch (error) {
|
|
7207
7369
|
console.error('Failed to install skills:', error);
|
|
7208
7370
|
res.status(500).json({ ok: false, error: { kind: 'unknown', message: error.message || 'Failed to install skills' } });
|
|
@@ -7217,7 +7379,9 @@ async function main(options = {}) {
|
|
|
7217
7379
|
if (!directory) {
|
|
7218
7380
|
return res.status(400).json({ error });
|
|
7219
7381
|
}
|
|
7220
|
-
const
|
|
7382
|
+
const discoveredSkill = ((await fetchOpenCodeDiscoveredSkills(directory)) || [])
|
|
7383
|
+
.find((skill) => skill.name === skillName) || null;
|
|
7384
|
+
const sources = getSkillSources(skillName, directory, discoveredSkill);
|
|
7221
7385
|
|
|
7222
7386
|
res.json({
|
|
7223
7387
|
name: skillName,
|
|
@@ -7242,7 +7406,9 @@ async function main(options = {}) {
|
|
|
7242
7406
|
return res.status(400).json({ error });
|
|
7243
7407
|
}
|
|
7244
7408
|
|
|
7245
|
-
|
|
7409
|
+
const discoveredSkill = ((await fetchOpenCodeDiscoveredSkills(directory)) || [])
|
|
7410
|
+
.find((skill) => skill.name === skillName) || null;
|
|
7411
|
+
const sources = getSkillSources(skillName, directory, discoveredSkill);
|
|
7246
7412
|
if (!sources.md.exists || !sources.md.dir) {
|
|
7247
7413
|
return res.status(404).json({ error: 'Skill not found' });
|
|
7248
7414
|
}
|
|
@@ -7263,7 +7429,7 @@ async function main(options = {}) {
|
|
|
7263
7429
|
app.post('/api/config/skills/:name', async (req, res) => {
|
|
7264
7430
|
try {
|
|
7265
7431
|
const skillName = req.params.name;
|
|
7266
|
-
const { scope, ...config } = req.body;
|
|
7432
|
+
const { scope, source: skillSource, ...config } = req.body;
|
|
7267
7433
|
const { directory, error } = await resolveProjectDirectory(req);
|
|
7268
7434
|
if (!directory) {
|
|
7269
7435
|
return res.status(400).json({ error });
|
|
@@ -7272,13 +7438,14 @@ async function main(options = {}) {
|
|
|
7272
7438
|
console.log('[Server] Creating skill:', skillName);
|
|
7273
7439
|
console.log('[Server] Scope:', scope, 'Working directory:', directory);
|
|
7274
7440
|
|
|
7275
|
-
createSkill(skillName, config, directory, scope);
|
|
7276
|
-
|
|
7441
|
+
createSkill(skillName, { ...config, source: skillSource }, directory, scope);
|
|
7442
|
+
await refreshOpenCodeAfterConfigChange('skill creation');
|
|
7277
7443
|
|
|
7278
7444
|
res.json({
|
|
7279
7445
|
success: true,
|
|
7280
|
-
requiresReload:
|
|
7281
|
-
message: `Skill ${skillName} created successfully
|
|
7446
|
+
requiresReload: true,
|
|
7447
|
+
message: `Skill ${skillName} created successfully. Reloading interface…`,
|
|
7448
|
+
reloadDelayMs: CLIENT_RELOAD_DELAY_MS,
|
|
7282
7449
|
});
|
|
7283
7450
|
} catch (error) {
|
|
7284
7451
|
console.error('Failed to create skill:', error);
|
|
@@ -7300,12 +7467,13 @@ async function main(options = {}) {
|
|
|
7300
7467
|
console.log('[Server] Working directory:', directory);
|
|
7301
7468
|
|
|
7302
7469
|
updateSkill(skillName, updates, directory);
|
|
7303
|
-
|
|
7470
|
+
await refreshOpenCodeAfterConfigChange('skill update');
|
|
7304
7471
|
|
|
7305
7472
|
res.json({
|
|
7306
7473
|
success: true,
|
|
7307
|
-
requiresReload:
|
|
7308
|
-
message: `Skill ${skillName} updated successfully
|
|
7474
|
+
requiresReload: true,
|
|
7475
|
+
message: `Skill ${skillName} updated successfully. Reloading interface…`,
|
|
7476
|
+
reloadDelayMs: CLIENT_RELOAD_DELAY_MS,
|
|
7309
7477
|
});
|
|
7310
7478
|
} catch (error) {
|
|
7311
7479
|
console.error('[Server] Failed to update skill:', error);
|
|
@@ -7324,7 +7492,9 @@ async function main(options = {}) {
|
|
|
7324
7492
|
return res.status(400).json({ error });
|
|
7325
7493
|
}
|
|
7326
7494
|
|
|
7327
|
-
const
|
|
7495
|
+
const discoveredSkill = ((await fetchOpenCodeDiscoveredSkills(directory)) || [])
|
|
7496
|
+
.find((skill) => skill.name === skillName) || null;
|
|
7497
|
+
const sources = getSkillSources(skillName, directory, discoveredSkill);
|
|
7328
7498
|
if (!sources.md.exists || !sources.md.dir) {
|
|
7329
7499
|
return res.status(404).json({ error: 'Skill not found' });
|
|
7330
7500
|
}
|
|
@@ -7351,7 +7521,9 @@ async function main(options = {}) {
|
|
|
7351
7521
|
return res.status(400).json({ error });
|
|
7352
7522
|
}
|
|
7353
7523
|
|
|
7354
|
-
const
|
|
7524
|
+
const discoveredSkill = ((await fetchOpenCodeDiscoveredSkills(directory)) || [])
|
|
7525
|
+
.find((skill) => skill.name === skillName) || null;
|
|
7526
|
+
const sources = getSkillSources(skillName, directory, discoveredSkill);
|
|
7355
7527
|
if (!sources.md.exists || !sources.md.dir) {
|
|
7356
7528
|
return res.status(404).json({ error: 'Skill not found' });
|
|
7357
7529
|
}
|
|
@@ -7378,12 +7550,13 @@ async function main(options = {}) {
|
|
|
7378
7550
|
}
|
|
7379
7551
|
|
|
7380
7552
|
deleteSkill(skillName, directory);
|
|
7381
|
-
|
|
7553
|
+
await refreshOpenCodeAfterConfigChange('skill deletion');
|
|
7382
7554
|
|
|
7383
7555
|
res.json({
|
|
7384
7556
|
success: true,
|
|
7385
|
-
requiresReload:
|
|
7386
|
-
message: `Skill ${skillName} deleted successfully
|
|
7557
|
+
requiresReload: true,
|
|
7558
|
+
message: `Skill ${skillName} deleted successfully. Reloading interface…`,
|
|
7559
|
+
reloadDelayMs: CLIENT_RELOAD_DELAY_MS,
|
|
7387
7560
|
});
|
|
7388
7561
|
} catch (error) {
|
|
7389
7562
|
console.error('Failed to delete skill:', error);
|