@hybridlabor-api/aos 4.11.0 → 4.12.0
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/bin/aos-store.mjs +29 -59
- package/installer.js +39 -2
- package/lib/ecc-store-index.json +1 -1
- package/lib/store-shared.mjs +133 -0
- package/lib/store-ui/index.html +150 -0
- package/lib/store-ui/server.mjs +180 -0
- package/package.json +2 -2
- package/scripts/build-ecc-store-index.mjs +17 -1
- package/skills/bdb-aos/SKILL.md +46 -0
- package/skills/global_config/aos-store/SKILL.md +71 -0
package/bin/aos-store.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { dirname, join, resolve } from 'node:path';
|
|
7
6
|
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { fetchSkillFiles, fileTargets, localAgents, localSkills, targetsFor as sharedTargetsFor, validateFiles, writeSkillFiles } from '../lib/store-shared.mjs';
|
|
8
8
|
|
|
9
9
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
10
|
-
const INDEX_PATH = join(ROOT, 'lib', 'ecc-store-index.json');
|
|
10
|
+
const INDEX_PATH = process.env.AOS_STORE_INDEX || join(ROOT, 'lib', 'ecc-store-index.json');
|
|
11
|
+
const RAW_BASE = (process.env.AOS_STORE_RAW_BASE || 'https://raw.githubusercontent.com/affaan-m/ECC').replace(/\/+$/, '');
|
|
11
12
|
const index = JSON.parse(readFileSync(INDEX_PATH, 'utf8'));
|
|
12
13
|
const args = process.argv.slice(2);
|
|
13
14
|
const dryRun = args.includes('--dry-run');
|
|
@@ -33,49 +34,8 @@ function findItem(name) {
|
|
|
33
34
|
return null;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const visit = (dir) => {
|
|
39
|
-
if (!existsSync(dir)) return;
|
|
40
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
41
|
-
const fullPath = join(dir, entry.name);
|
|
42
|
-
if (entry.isDirectory()) visit(fullPath);
|
|
43
|
-
else if (entry.name === 'SKILL.md') names.add(basename(dirname(fullPath)));
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
visit(join(ROOT, 'skills'));
|
|
47
|
-
return names;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function localAgentNames() {
|
|
51
|
-
const names = new Set();
|
|
52
|
-
for (const dir of [join(ROOT, '.claude', 'agents'), join(ROOT, '.agents', 'agents'), join(ROOT, 'agents')]) {
|
|
53
|
-
if (!existsSync(dir)) continue;
|
|
54
|
-
for (const entry of readdirSync(dir)) names.add(entry.replace(/\.md$/i, ''));
|
|
55
|
-
}
|
|
56
|
-
return names;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function globalRoots(type) {
|
|
60
|
-
const home = homedir();
|
|
61
|
-
return type === 'skills'
|
|
62
|
-
? [
|
|
63
|
-
join(home, '.agents', 'skills'),
|
|
64
|
-
join(home, '.claude', 'skills'),
|
|
65
|
-
join(home, '.codex', 'skills'),
|
|
66
|
-
join(home, '.cursor', 'skills'),
|
|
67
|
-
join(home, '.roo', 'skills'),
|
|
68
|
-
join(home, '.gemini', 'config', 'skills'),
|
|
69
|
-
]
|
|
70
|
-
: [
|
|
71
|
-
join(home, '.agents', 'agents'),
|
|
72
|
-
join(home, '.claude', 'agents'),
|
|
73
|
-
join(home, '.codex', 'agents'),
|
|
74
|
-
join(home, '.cursor', 'agents'),
|
|
75
|
-
join(home, '.roo', 'agents'),
|
|
76
|
-
join(home, '.gemini', 'config', 'agents'),
|
|
77
|
-
];
|
|
78
|
-
}
|
|
37
|
+
const localSkillNames = () => new Set(localSkills(ROOT).keys());
|
|
38
|
+
const localAgentNames = () => new Set(localAgents(ROOT).keys());
|
|
79
39
|
|
|
80
40
|
function printItems(items) {
|
|
81
41
|
if (items.length === 0) {
|
|
@@ -90,22 +50,13 @@ function printItems(items) {
|
|
|
90
50
|
}
|
|
91
51
|
|
|
92
52
|
async function download(upstreamPath) {
|
|
93
|
-
const url =
|
|
53
|
+
const url = `${RAW_BASE}/${index.pinned_commit}/${upstreamPath}`;
|
|
94
54
|
const response = await fetch(url, { headers: { 'user-agent': 'aos-store' } });
|
|
95
55
|
if (!response.ok) throw new Error(`Download failed (${response.status}): ${url}`);
|
|
96
56
|
return Buffer.from(await response.arrayBuffer());
|
|
97
57
|
}
|
|
98
58
|
|
|
99
|
-
|
|
100
|
-
if (project) {
|
|
101
|
-
return type === 'skills'
|
|
102
|
-
? [join(process.cwd(), 'skills', name, 'SKILL.md')]
|
|
103
|
-
: [join(process.cwd(), 'agents', `${name}.md`)];
|
|
104
|
-
}
|
|
105
|
-
return globalRoots(type).map((root) => type === 'skills'
|
|
106
|
-
? join(root, name, 'SKILL.md')
|
|
107
|
-
: join(root, `${name}.md`));
|
|
108
|
-
}
|
|
59
|
+
const targetsFor = (type, name) => sharedTargetsFor(type, name, { project });
|
|
109
60
|
|
|
110
61
|
async function install(name) {
|
|
111
62
|
if (!/^[A-Za-z0-9_-]+$/.test(name)) throw new Error(`Invalid store name: ${name}`);
|
|
@@ -118,13 +69,26 @@ async function install(name) {
|
|
|
118
69
|
return;
|
|
119
70
|
}
|
|
120
71
|
const targets = targetsFor(type, name);
|
|
72
|
+
const multi = type === 'skills' && item.files?.length > 1;
|
|
73
|
+
if (multi) validateFiles(item.files);
|
|
121
74
|
if (dryRun) {
|
|
122
|
-
for (const target of targets)
|
|
75
|
+
for (const target of targets) {
|
|
76
|
+
if (multi) for (const file of fileTargets(target, item.files)) console.log(`[dry-run] write ${file}`);
|
|
77
|
+
else console.log(`[dry-run] write ${target}`);
|
|
78
|
+
}
|
|
123
79
|
return;
|
|
124
80
|
}
|
|
125
81
|
if (!net) {
|
|
126
82
|
throw new Error(`Downloading '${name}' requires internet access to fetch from upstream GitHub. Pass --net to confirm: aos store install ${name} --net`);
|
|
127
83
|
}
|
|
84
|
+
if (multi) {
|
|
85
|
+
const files = await fetchSkillFiles(RAW_BASE, index.pinned_commit, dirname(item.upstream_path), item.files);
|
|
86
|
+
for (const target of targets) {
|
|
87
|
+
const written = writeSkillFiles(target, files);
|
|
88
|
+
console.log(`Installed ${name} (${written.length} files) -> ${dirname(target)}`);
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
128
92
|
const content = await download(item.upstream_path);
|
|
129
93
|
const digest = createHash('sha256').update(content).digest('hex');
|
|
130
94
|
if (digest !== item.sha256) throw new Error(`SHA-256 mismatch for ${name}: expected ${item.sha256}, got ${digest}`);
|
|
@@ -140,6 +104,7 @@ function usage() {
|
|
|
140
104
|
console.log(' aos store list [--type=skills|agents]');
|
|
141
105
|
console.log(' aos store search <query>');
|
|
142
106
|
console.log(' aos store install <name> [--project] [--dry-run] [--net]');
|
|
107
|
+
console.log(' aos store ui [--port=N] [--no-open]');
|
|
143
108
|
}
|
|
144
109
|
|
|
145
110
|
async function main() {
|
|
@@ -156,6 +121,11 @@ async function main() {
|
|
|
156
121
|
printItems(items);
|
|
157
122
|
return;
|
|
158
123
|
}
|
|
124
|
+
if (command === 'ui') {
|
|
125
|
+
const { startUi } = await import('../lib/store-ui/server.mjs');
|
|
126
|
+
await startUi({ argv: args });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
159
129
|
if (command === 'install') {
|
|
160
130
|
if (!positional[1]) throw new Error('Install requires a store item name.');
|
|
161
131
|
await install(positional[1]);
|
package/installer.js
CHANGED
|
@@ -4520,6 +4520,16 @@ function generateAndOpenLaunchpad(installedModules = []) {
|
|
|
4520
4520
|
background-color: var(--green);
|
|
4521
4521
|
box-shadow: 0 0 10px rgba(63, 185, 80, 0.6);
|
|
4522
4522
|
}
|
|
4523
|
+
.card-hint {
|
|
4524
|
+
display: none;
|
|
4525
|
+
font-size: 11px;
|
|
4526
|
+
color: var(--text-muted);
|
|
4527
|
+
margin-top: 4px;
|
|
4528
|
+
font-style: italic;
|
|
4529
|
+
}
|
|
4530
|
+
.card.offline .card-hint {
|
|
4531
|
+
display: block;
|
|
4532
|
+
}
|
|
4523
4533
|
.footer {
|
|
4524
4534
|
margin-top: 28px;
|
|
4525
4535
|
text-align: center;
|
|
@@ -4691,6 +4701,25 @@ function generateAndOpenLaunchpad(installedModules = []) {
|
|
|
4691
4701
|
<span class="status-dot" id="dot-openwiki" title="Checking..."></span>
|
|
4692
4702
|
</div>
|
|
4693
4703
|
</a>
|
|
4704
|
+
<a class="card" href="http://127.0.0.1:4322" target="_blank">
|
|
4705
|
+
<div class="card-content">
|
|
4706
|
+
<div class="card-icon"><svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
|
4707
|
+
<defs><linearGradient id="grad-store" x1="50%" y1="0%" x2="50%" y2="100%"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#9b30c4"/></linearGradient></defs>
|
|
4708
|
+
<rect x="20" y="20" width="60" height="60" rx="4" fill="url(#grad-store)" opacity="0.85"/>
|
|
4709
|
+
<rect x="28" y="28" width="12" height="14" rx="1.5" fill="#0a0a0a" opacity="0.6"/><rect x="45" y="28" width="12" height="14" rx="1.5" fill="#0a0a0a" opacity="0.6"/><rect x="62" y="28" width="12" height="14" rx="1.5" fill="#0a0a0a" opacity="0.6"/>
|
|
4710
|
+
<rect x="28" y="48" width="12" height="14" rx="1.5" fill="#0a0a0a" opacity="0.6"/><rect x="45" y="48" width="12" height="14" rx="1.5" fill="#0a0a0a" opacity="0.6"/><rect x="62" y="48" width="12" height="14" rx="1.5" fill="#0a0a0a" opacity="0.6"/>
|
|
4711
|
+
</svg></div>
|
|
4712
|
+
<div class="card-info">
|
|
4713
|
+
<h2>AOS Store</h2>
|
|
4714
|
+
<p>Browse & install skills and agents</p>
|
|
4715
|
+
<div class="card-hint">offline — run: aos-store ui</div>
|
|
4716
|
+
</div>
|
|
4717
|
+
</div>
|
|
4718
|
+
<div class="card-meta">
|
|
4719
|
+
<span class="port-pill">:4322</span>
|
|
4720
|
+
<span class="status-dot" id="dot-store" title="Checking..."></span>
|
|
4721
|
+
</div>
|
|
4722
|
+
</a>
|
|
4694
4723
|
</div>
|
|
4695
4724
|
<div class="footer">
|
|
4696
4725
|
<span>Autostart Daemons • 127.0.0.1</span>
|
|
@@ -4700,9 +4729,16 @@ function generateAndOpenLaunchpad(installedModules = []) {
|
|
|
4700
4729
|
function checkHealth(url, dotId) {
|
|
4701
4730
|
const dot = document.getElementById(dotId);
|
|
4702
4731
|
if (!dot) return;
|
|
4732
|
+
const card = dot.closest('.card');
|
|
4703
4733
|
fetch(url, { mode: 'no-cors' })
|
|
4704
|
-
.then(() =>
|
|
4705
|
-
|
|
4734
|
+
.then(() => {
|
|
4735
|
+
dot.classList.add('online');
|
|
4736
|
+
if (card) card.classList.remove('offline');
|
|
4737
|
+
})
|
|
4738
|
+
.catch(() => {
|
|
4739
|
+
dot.classList.remove('online');
|
|
4740
|
+
if (card) card.classList.add('offline');
|
|
4741
|
+
});
|
|
4706
4742
|
}
|
|
4707
4743
|
function checkAllHealth() {
|
|
4708
4744
|
checkHealth('http://127.0.0.1:8088', 'dot-memb');
|
|
@@ -4710,6 +4746,7 @@ function generateAndOpenLaunchpad(installedModules = []) {
|
|
|
4710
4746
|
checkHealth('http://127.0.0.1:3101', 'dot-ao');
|
|
4711
4747
|
checkHealth('http://127.0.0.1:9080', 'dot-remote');
|
|
4712
4748
|
checkHealth('http://127.0.0.1:4321', 'dot-openwiki');
|
|
4749
|
+
checkHealth('http://127.0.0.1:4322', 'dot-store');
|
|
4713
4750
|
}
|
|
4714
4751
|
checkAllHealth();
|
|
4715
4752
|
setInterval(checkAllHealth, 5000);
|