@tom2012/cc-web 2026.4.19-t → 2026.4.19-u
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/README.md +1 -1
- package/backend/dist/adapters/claude-adapter.d.ts.map +1 -1
- package/backend/dist/adapters/claude-adapter.js +107 -1
- package/backend/dist/adapters/claude-adapter.js.map +1 -1
- package/backend/dist/adapters/gemini-adapter.js +1 -1
- package/backend/dist/adapters/gemini-adapter.js.map +1 -1
- package/backend/dist/adapters/types.d.ts +3 -0
- package/backend/dist/adapters/types.d.ts.map +1 -1
- package/backend/dist/routes/skillhub.d.ts +27 -0
- package/backend/dist/routes/skillhub.d.ts.map +1 -1
- package/backend/dist/routes/skillhub.js +149 -180
- package/backend/dist/routes/skillhub.js.map +1 -1
- package/backend/dist/sync-service.d.ts +9 -6
- package/backend/dist/sync-service.d.ts.map +1 -1
- package/backend/dist/sync-service.js +84 -14
- package/backend/dist/sync-service.js.map +1 -1
- package/backend/package-lock.json +26 -0
- package/backend/package.json +2 -0
- package/frontend/dist/assets/{AssistantMessageContent-D1-3VpWE.js → AssistantMessageContent-DuOuKxgH.js} +2 -2
- package/frontend/dist/assets/{GraphPreview-CeI4sbtV.js → GraphPreview-UVA4ODDv.js} +1 -1
- package/frontend/dist/assets/MobilePage-8bMFdgD9.js +14 -0
- package/frontend/dist/assets/{OfficePreview-Dgs4aiYi.js → OfficePreview-DFa568OD.js} +2 -2
- package/frontend/dist/assets/{ProjectPage-DPv57nD9.js → ProjectPage-D23Cx4LX.js} +5 -5
- package/frontend/dist/assets/SettingsPage-CTTCafJA.js +13 -0
- package/frontend/dist/assets/SkillHubPage-BBWaNxzY.js +13 -0
- package/frontend/dist/assets/{chevron-down-CyDTNuVw.js → chevron-down-XjVVezAn.js} +1 -1
- package/frontend/dist/assets/{chevron-up-BURz786o.js → chevron-up-BD4j4icv.js} +1 -1
- package/frontend/dist/assets/index-BL3iZx_u.css +1 -0
- package/frontend/dist/assets/{index-z4qRGojt.js → index-Cz7UGH1l.js} +1 -1
- package/frontend/dist/assets/index-Qowsi3Ey.js +13 -0
- package/frontend/dist/assets/{index-BFWZX3Hz.js → index-hLxTQaCY.js} +10 -10
- package/frontend/dist/assets/{jszip.min-CPG_u-b-.js → jszip.min-BvfmA-CV.js} +1 -1
- package/frontend/dist/assets/{search-CZ83atP-.js → search-CdJtkP3F.js} +1 -1
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/MobilePage-BP7c3W3D.js +0 -14
- package/frontend/dist/assets/SettingsPage-CFSmKovg.js +0 -13
- package/frontend/dist/assets/SkillHubPage-4g29B-Hr.js +0 -13
- package/frontend/dist/assets/index-D0NB3R9q.css +0 -1
- package/frontend/dist/assets/index-Uy-V98k3.js +0 -13
|
@@ -34,34 +34,39 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
const express_1 = require("express");
|
|
37
|
-
const config_1 = require("../config");
|
|
38
|
-
const uuid_1 = require("uuid");
|
|
39
37
|
const https = __importStar(require("https"));
|
|
38
|
+
const yaml = __importStar(require("js-yaml"));
|
|
39
|
+
/**
|
|
40
|
+
* CCWeb Hub proxy.
|
|
41
|
+
*
|
|
42
|
+
* Serves a read-only view of `zbc0315/ccweb-hub`, which stores community-shared
|
|
43
|
+
* Quick Prompts (Shortcuts) and Agent Prompts as individual `.md` files in
|
|
44
|
+
* `quick-prompts/` and `agent-prompts/` directories. Each file has YAML
|
|
45
|
+
* frontmatter (`label`, `kind`, `author`, `tags`, `description`) and a body
|
|
46
|
+
* that is the prompt itself.
|
|
47
|
+
*
|
|
48
|
+
* Submission is handled client-side via a pre-filled GitHub Issue URL — there
|
|
49
|
+
* is no longer any write path through this server (avoids pitfalls #30: no
|
|
50
|
+
* token bundled in the npm package, ever).
|
|
51
|
+
*
|
|
52
|
+
* Plugin Hub (`/plugins` endpoint) still proxies `plugins.json` at repo root
|
|
53
|
+
* for the separate PluginDock install flow.
|
|
54
|
+
*/
|
|
40
55
|
const router = (0, express_1.Router)();
|
|
41
56
|
const REPO_OWNER = 'zbc0315';
|
|
42
|
-
const REPO_NAME = 'ccweb-
|
|
57
|
+
const REPO_NAME = 'ccweb-hub';
|
|
43
58
|
const RAW_BASE = `https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/main`;
|
|
44
59
|
const API_BASE = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}`;
|
|
45
|
-
// ──
|
|
46
|
-
// SkillHub write operations (submit issue, download-count bump) require a GitHub
|
|
47
|
-
// PAT scoped to the SkillHub repo. The token MUST come from the CCWEB_GITHUB_TOKEN
|
|
48
|
-
// environment variable — never bundled in the npm package, since any installed
|
|
49
|
-
// user would be able to extract it from backend/dist and gain write access to
|
|
50
|
-
// the SkillHub repo (which in turn would poison every ccweb client via
|
|
51
|
-
// plugins.json / skills.json).
|
|
60
|
+
// ── HTTP ─────────────────────────────────────────────────────────────────────
|
|
52
61
|
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
let cachedSkills = null;
|
|
60
|
-
let cacheTime = 0;
|
|
61
|
-
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
62
|
-
function httpGet(url, maxRedirects = 5) {
|
|
62
|
+
// Two distinct helpers: `apiGet` talks to the GitHub v3 API (JSON, versioned
|
|
63
|
+
// Accept header); `rawGet` fetches raw file content with no content negotiation
|
|
64
|
+
// assumption. Previously a single helper sent the API Accept header for raw
|
|
65
|
+
// requests too — harmless today but would break if GitHub raw ever starts to
|
|
66
|
+
// respect the Accept header strictly.
|
|
67
|
+
function request(url, accept, maxRedirects = 5) {
|
|
63
68
|
return new Promise((resolve, reject) => {
|
|
64
|
-
https.get(url, { headers: { 'User-Agent': 'CCWeb' } }, (res) => {
|
|
69
|
+
https.get(url, { headers: { 'User-Agent': 'CCWeb', 'Accept': accept } }, (res) => {
|
|
65
70
|
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
66
71
|
if (maxRedirects <= 0) {
|
|
67
72
|
reject(new Error('Too many redirects'));
|
|
@@ -72,14 +77,14 @@ function httpGet(url, maxRedirects = 5) {
|
|
|
72
77
|
reject(new Error('Redirect to non-HTTPS URL blocked'));
|
|
73
78
|
return;
|
|
74
79
|
}
|
|
75
|
-
|
|
80
|
+
request(loc, accept, maxRedirects - 1).then(resolve, reject);
|
|
76
81
|
return;
|
|
77
82
|
}
|
|
78
83
|
let data = '';
|
|
79
84
|
res.on('data', (chunk) => { data += chunk; });
|
|
80
85
|
res.on('end', () => {
|
|
81
86
|
if (res.statusCode && res.statusCode >= 400) {
|
|
82
|
-
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
87
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data.slice(0, 200)}`));
|
|
83
88
|
}
|
|
84
89
|
else {
|
|
85
90
|
resolve(data);
|
|
@@ -89,196 +94,160 @@ function httpGet(url, maxRedirects = 5) {
|
|
|
89
94
|
}).on('error', reject);
|
|
90
95
|
});
|
|
91
96
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
const apiGet = (url) => request(url, 'application/vnd.github.v3+json');
|
|
98
|
+
const rawGet = (url) => request(url, '*/*');
|
|
99
|
+
function parseMarkdown(raw) {
|
|
100
|
+
// Recognize `---\n<yaml>\n---\n<body>`; anything else → whole file is body.
|
|
101
|
+
const match = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/);
|
|
102
|
+
if (!match)
|
|
103
|
+
return { meta: {}, body: raw };
|
|
104
|
+
let meta = {};
|
|
105
|
+
try {
|
|
106
|
+
const loaded = yaml.load(match[1], { schema: yaml.FAILSAFE_SCHEMA });
|
|
107
|
+
if (loaded && typeof loaded === 'object')
|
|
108
|
+
meta = loaded;
|
|
96
109
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return new Promise((resolve, reject) => {
|
|
100
|
-
const req = https.request({
|
|
101
|
-
hostname: url.hostname,
|
|
102
|
-
path: url.pathname + url.search,
|
|
103
|
-
method,
|
|
104
|
-
headers: {
|
|
105
|
-
'User-Agent': 'CCWeb',
|
|
106
|
-
'Authorization': `Bearer ${token}`,
|
|
107
|
-
'Accept': 'application/vnd.github.v3+json',
|
|
108
|
-
'Content-Type': 'application/json',
|
|
109
|
-
'Content-Length': Buffer.byteLength(postData),
|
|
110
|
-
},
|
|
111
|
-
}, (res) => {
|
|
112
|
-
let data = '';
|
|
113
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
114
|
-
res.on('end', () => {
|
|
115
|
-
if (res.statusCode && res.statusCode >= 400) {
|
|
116
|
-
reject(new Error(`GitHub API ${res.statusCode}: ${data}`));
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
resolve(data);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
res.on('error', reject);
|
|
123
|
-
});
|
|
124
|
-
req.on('error', reject);
|
|
125
|
-
if (postData)
|
|
126
|
-
req.write(postData);
|
|
127
|
-
req.end();
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
async function fetchSkills(forceRefresh = false) {
|
|
131
|
-
const now = Date.now();
|
|
132
|
-
if (!forceRefresh && cachedSkills && (now - cacheTime) < CACHE_TTL) {
|
|
133
|
-
return cachedSkills;
|
|
110
|
+
catch {
|
|
111
|
+
// YAML malformed — treat as no frontmatter rather than crashing the hub
|
|
134
112
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
113
|
+
return { meta, body: match[2].trimStart() };
|
|
114
|
+
}
|
|
115
|
+
function coerceTags(raw) {
|
|
116
|
+
if (Array.isArray(raw)) {
|
|
117
|
+
const out = raw.filter((x) => typeof x === 'string');
|
|
118
|
+
return out.length ? out : undefined;
|
|
140
119
|
}
|
|
141
|
-
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
throw err;
|
|
120
|
+
if (typeof raw === 'string') {
|
|
121
|
+
// Single string → split on comma for forgiving input
|
|
122
|
+
const split = raw.split(',').map((s) => s.trim()).filter(Boolean);
|
|
123
|
+
return split.length ? split : undefined;
|
|
146
124
|
}
|
|
125
|
+
return undefined;
|
|
147
126
|
}
|
|
148
|
-
// ──
|
|
149
|
-
|
|
150
|
-
|
|
127
|
+
// ── Cache ────────────────────────────────────────────────────────────────────
|
|
128
|
+
let cachedItems = null;
|
|
129
|
+
let cacheTime = 0;
|
|
130
|
+
const CACHE_TTL = 5 * 60 * 1000;
|
|
131
|
+
async function listDir(dir) {
|
|
151
132
|
try {
|
|
152
|
-
const
|
|
153
|
-
|
|
133
|
+
const raw = await apiGet(`${API_BASE}/contents/${dir}`);
|
|
134
|
+
const parsed = JSON.parse(raw);
|
|
135
|
+
return Array.isArray(parsed)
|
|
136
|
+
? parsed.filter((f) => f && f.type === 'file' && f.name.endsWith('.md'))
|
|
137
|
+
: [];
|
|
154
138
|
}
|
|
155
|
-
catch
|
|
156
|
-
|
|
139
|
+
catch {
|
|
140
|
+
return []; // 404 = directory empty / absent — not an error
|
|
157
141
|
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
142
|
+
}
|
|
143
|
+
async function fetchKind(kind) {
|
|
144
|
+
const dir = kind === 'quick-prompt' ? 'quick-prompts' : 'agent-prompts';
|
|
145
|
+
const files = await listDir(dir);
|
|
146
|
+
if (files.length === 0)
|
|
147
|
+
return [];
|
|
148
|
+
// Raw fetches are on raw.githubusercontent.com (not counted against the
|
|
149
|
+
// 60/hr GitHub API anon limit), so parallelizing is safe.
|
|
150
|
+
const results = await Promise.allSettled(files.map(async (file) => {
|
|
151
|
+
if (!file.download_url)
|
|
152
|
+
return null;
|
|
153
|
+
try {
|
|
154
|
+
const content = await rawGet(file.download_url);
|
|
155
|
+
const { meta, body } = parseMarkdown(content);
|
|
156
|
+
const basename = file.name.replace(/\.md$/, '');
|
|
157
|
+
const label = typeof meta.label === 'string' && meta.label ? meta.label : basename;
|
|
158
|
+
return {
|
|
159
|
+
id: `${kind}/${basename}`,
|
|
160
|
+
kind,
|
|
161
|
+
label,
|
|
162
|
+
body: body.trim(),
|
|
163
|
+
author: typeof meta.author === 'string' ? meta.author : undefined,
|
|
164
|
+
tags: coerceTags(meta.tags),
|
|
165
|
+
description: typeof meta.description === 'string' ? meta.description : undefined,
|
|
166
|
+
file: `${dir}/${file.name}`,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return null; // One bad file can't poison the whole kind
|
|
171
|
+
}
|
|
172
|
+
}));
|
|
173
|
+
return results
|
|
174
|
+
.map((r) => (r.status === 'fulfilled' ? r.value : null))
|
|
175
|
+
.filter((x) => x !== null);
|
|
176
|
+
}
|
|
177
|
+
async function fetchAllItems(forceRefresh = false) {
|
|
178
|
+
const now = Date.now();
|
|
179
|
+
if (!forceRefresh && cachedItems && (now - cacheTime) < CACHE_TTL) {
|
|
180
|
+
return cachedItems;
|
|
165
181
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
182
|
+
// Resolve each kind independently — a transient failure on one directory
|
|
183
|
+
// should not make the whole hub appear broken.
|
|
184
|
+
const [quickRes, agentRes] = await Promise.allSettled([
|
|
185
|
+
fetchKind('quick-prompt'),
|
|
186
|
+
fetchKind('agent-prompt'),
|
|
187
|
+
]);
|
|
188
|
+
const quick = quickRes.status === 'fulfilled' ? quickRes.value : [];
|
|
189
|
+
const agent = agentRes.status === 'fulfilled' ? agentRes.value : [];
|
|
190
|
+
if (quickRes.status === 'rejected')
|
|
191
|
+
console.warn('[ccweb-hub] quick-prompts fetch failed:', quickRes.reason);
|
|
192
|
+
if (agentRes.status === 'rejected')
|
|
193
|
+
console.warn('[ccweb-hub] agent-prompts fetch failed:', agentRes.reason);
|
|
194
|
+
cachedItems = [...quick, ...agent];
|
|
195
|
+
cacheTime = now;
|
|
196
|
+
return cachedItems;
|
|
197
|
+
}
|
|
198
|
+
// ── Routes ───────────────────────────────────────────────────────────────────
|
|
199
|
+
// GET /items — unified list of Quick Prompts + Agent Prompts in ccweb-hub
|
|
200
|
+
router.get('/items', async (_req, res) => {
|
|
177
201
|
try {
|
|
178
|
-
await
|
|
179
|
-
|
|
180
|
-
body: issueBody,
|
|
181
|
-
labels: ['new-skill'],
|
|
182
|
-
});
|
|
183
|
-
res.json({ success: true });
|
|
202
|
+
const items = await fetchAllItems();
|
|
203
|
+
res.json(items);
|
|
184
204
|
}
|
|
185
205
|
catch (err) {
|
|
186
|
-
res.status(502).json({ error: 'Failed to
|
|
206
|
+
res.status(502).json({ error: 'Failed to fetch ccweb-hub', detail: err.message });
|
|
187
207
|
}
|
|
188
208
|
});
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
209
|
+
// GET /skills — legacy alias for pre-hub clients. Returns the Quick Prompts
|
|
210
|
+
// subset in the old SkillHubItem shape. `createdAt` is set to current ISO so
|
|
211
|
+
// clients that sort by date don't choke on empty strings.
|
|
212
|
+
router.get('/skills', async (_req, res) => {
|
|
193
213
|
try {
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
current = current.parentId ? skills.find((s) => s.id === current.parentId) : undefined;
|
|
210
|
-
}
|
|
211
|
-
// Add all skills in the chain to global shortcuts, preserving parentId links
|
|
212
|
-
const shortcuts = (0, config_1.getGlobalShortcuts)(username);
|
|
213
|
-
// Map from SkillHub id → local shortcut id
|
|
214
|
-
const idMap = new Map();
|
|
215
|
-
for (const item of chain) {
|
|
216
|
-
// Check if already exists
|
|
217
|
-
const existing = shortcuts.find((s) => s.label === item.label && s.command === item.command);
|
|
218
|
-
if (existing) {
|
|
219
|
-
idMap.set(item.id, existing.id);
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
const localId = (0, uuid_1.v4)();
|
|
223
|
-
const localParentId = item.parentId ? idMap.get(item.parentId) : undefined;
|
|
224
|
-
const newItem = { id: localId, label: item.label, command: item.command };
|
|
225
|
-
if (localParentId)
|
|
226
|
-
newItem.parentId = localParentId;
|
|
227
|
-
shortcuts.push(newItem);
|
|
228
|
-
idMap.set(item.id, localId);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
(0, config_1.saveGlobalShortcuts)(shortcuts, username);
|
|
232
|
-
const newShortcut = shortcuts.find((s) => s.id === idMap.get(skill.id));
|
|
233
|
-
// Try to update download count in repo (best-effort, don't fail the request)
|
|
234
|
-
try {
|
|
235
|
-
// Get current skills.json content and sha
|
|
236
|
-
const fileResp = await githubApi('GET', '/contents/skills.json');
|
|
237
|
-
const fileData = JSON.parse(fileResp);
|
|
238
|
-
const content = Buffer.from(fileData.content, 'base64').toString('utf-8');
|
|
239
|
-
const repoSkills = JSON.parse(content);
|
|
240
|
-
const target = repoSkills.find((s) => s.id === id);
|
|
241
|
-
if (target) {
|
|
242
|
-
target.downloads = (target.downloads || 0) + 1;
|
|
243
|
-
const updated = Buffer.from(JSON.stringify(repoSkills, null, 2) + '\n').toString('base64');
|
|
244
|
-
await githubApi('PUT', '/contents/skills.json', {
|
|
245
|
-
message: `bump download count for ${id}`,
|
|
246
|
-
content: updated,
|
|
247
|
-
sha: fileData.sha,
|
|
248
|
-
});
|
|
249
|
-
// Update cache
|
|
250
|
-
cachedSkills = repoSkills;
|
|
251
|
-
cacheTime = Date.now();
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
catch {
|
|
255
|
-
// Silently ignore download count update failures
|
|
256
|
-
}
|
|
257
|
-
res.json(newShortcut);
|
|
214
|
+
const items = await fetchAllItems();
|
|
215
|
+
const iso = new Date().toISOString();
|
|
216
|
+
const legacy = items
|
|
217
|
+
.filter((i) => i.kind === 'quick-prompt')
|
|
218
|
+
.map((i) => ({
|
|
219
|
+
id: i.id,
|
|
220
|
+
label: i.label,
|
|
221
|
+
command: i.body,
|
|
222
|
+
description: i.description ?? '',
|
|
223
|
+
author: i.author ?? 'anonymous',
|
|
224
|
+
tags: i.tags ?? [],
|
|
225
|
+
downloads: 0,
|
|
226
|
+
createdAt: iso,
|
|
227
|
+
}));
|
|
228
|
+
res.json(legacy);
|
|
258
229
|
}
|
|
259
230
|
catch (err) {
|
|
260
|
-
res.status(
|
|
231
|
+
res.status(502).json({ error: 'Failed to fetch ccweb-hub', detail: err.message });
|
|
261
232
|
}
|
|
262
233
|
});
|
|
263
|
-
// ── Plugin Hub
|
|
234
|
+
// ── Plugin Hub (unchanged — proxies plugins.json at hub repo root) ─────────
|
|
264
235
|
let _pluginCache = null;
|
|
265
236
|
let _pluginCacheTime = 0;
|
|
266
|
-
const PLUGIN_CACHE_TTL = 5 * 60000;
|
|
237
|
+
const PLUGIN_CACHE_TTL = 5 * 60000;
|
|
267
238
|
router.get('/plugins', async (_req, res) => {
|
|
268
239
|
const now = Date.now();
|
|
269
240
|
if (_pluginCache && now - _pluginCacheTime < PLUGIN_CACHE_TTL) {
|
|
270
241
|
return res.json(_pluginCache);
|
|
271
242
|
}
|
|
272
243
|
try {
|
|
273
|
-
const
|
|
274
|
-
const data = await httpGet(url);
|
|
244
|
+
const data = await rawGet(`${RAW_BASE}/plugins.json`);
|
|
275
245
|
const plugins = JSON.parse(data);
|
|
276
246
|
_pluginCache = Array.isArray(plugins) ? plugins : [];
|
|
277
247
|
_pluginCacheTime = now;
|
|
278
248
|
res.json(_pluginCache);
|
|
279
249
|
}
|
|
280
250
|
catch {
|
|
281
|
-
// Return stale cache or empty
|
|
282
251
|
res.json(_pluginCache ?? []);
|
|
283
252
|
}
|
|
284
253
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skillhub.js","sourceRoot":"","sources":["../../src/routes/skillhub.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAAoD;AACpD,
|
|
1
|
+
{"version":3,"file":"skillhub.js","sourceRoot":"","sources":["../../src/routes/skillhub.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,11 +15,15 @@ import { type SyncDirection } from './sync-config';
|
|
|
15
15
|
* - **Concurrency**: one in-flight sync per (user, project); subsequent calls return
|
|
16
16
|
* `{ skipped: true }`.
|
|
17
17
|
* - **Logs**: written via createWriteStream (ordered); file truncated to keep the last
|
|
18
|
-
* ~20 runs, preventing long-term growth
|
|
19
|
-
* the hundreds of lines --progress emitted).
|
|
18
|
+
* ~20 runs, preventing long-term growth.
|
|
20
19
|
* - **bidirectional**: is NOT a safe two-way sync (rsync can't). The push leg is run
|
|
21
20
|
* without --delete and with -u (update-if-newer), so remote-newer files survive the
|
|
22
21
|
* pull leg that follows. Deletions must be handled manually.
|
|
22
|
+
* - **openrsync (macOS 15+)**: shipped at `/usr/bin/rsync`, protocol 29. Doesn't support
|
|
23
|
+
* `--stats`; `-v` output doesn't include per-file lines. We use `-avzi` (itemize
|
|
24
|
+
* changes) which works on both GNU and openrsync, and parses telemetry from formats
|
|
25
|
+
* both emit (`>f`/`<f`-prefixed lines and the `total size is N` tail). A homebrew
|
|
26
|
+
* GNU rsync at `/opt/homebrew/bin/rsync` is preferred when present for richer output.
|
|
23
27
|
*/
|
|
24
28
|
export interface SyncResult {
|
|
25
29
|
ok: boolean;
|
|
@@ -39,10 +43,9 @@ export declare function syncProject(username: string, projectId: string, project
|
|
|
39
43
|
export declare function isSyncing(username: string, projectId: string): boolean;
|
|
40
44
|
export declare function listInFlight(username: string): string[];
|
|
41
45
|
/**
|
|
42
|
-
* Non-destructive connection test: runs
|
|
43
|
-
*
|
|
44
|
-
* SSH setup will also succeed.
|
|
45
|
-
* of `--version` (which doesn't touch ssh at all).
|
|
46
|
+
* Non-destructive connection test: runs the wrapper script directly with
|
|
47
|
+
* `<user@host> true`. Uses the identical exec path as rsync's `-e`, so if
|
|
48
|
+
* this succeeds the sync's SSH setup will also succeed.
|
|
46
49
|
*/
|
|
47
50
|
export declare function testConnection(username: string): Promise<{
|
|
48
51
|
ok: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-service.d.ts","sourceRoot":"","sources":["../src/sync-service.ts"],"names":[],"mappings":"AAMA,OAAO,EAEY,KAAK,aAAa,EACpC,MAAM,eAAe,CAAC;AAEvB
|
|
1
|
+
{"version":3,"file":"sync-service.d.ts","sourceRoot":"","sources":["../src/sync-service.ts"],"names":[],"mappings":"AAMA,OAAO,EAEY,KAAK,aAAa,EACpC,MAAM,eAAe,CAAC;AAEvB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAkSD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,iBAAiB,CAAC,EAAE,aAAa,GAChC,OAAO,CAAC,UAAU,CAAC,CA6DrB;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAOvD;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAwChG"}
|