oh-skillhub 0.1.16 → 0.1.17
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/package.json +1 -1
- package/src/source.js +65 -2
package/package.json
CHANGED
package/src/source.js
CHANGED
|
@@ -4,6 +4,10 @@ const fs = require("node:fs");
|
|
|
4
4
|
const os = require("node:os");
|
|
5
5
|
const path = require("node:path");
|
|
6
6
|
|
|
7
|
+
const DEFAULT_MOVE_RETRIES = 8;
|
|
8
|
+
const DEFAULT_MOVE_DELAY_MS = 120;
|
|
9
|
+
const RETRYABLE_MOVE_CODES = new Set(["EBUSY", "EPERM", "ENOTEMPTY"]);
|
|
10
|
+
|
|
7
11
|
function ensureSkillSourceRoot(manifest, options = {}) {
|
|
8
12
|
const env = options.env || process.env;
|
|
9
13
|
if (env.OH_SKILLHUB_SOURCE_DIR) {
|
|
@@ -44,7 +48,7 @@ function ensureSkillSourceRoot(manifest, options = {}) {
|
|
|
44
48
|
const detail = gitDetail(checkoutResult);
|
|
45
49
|
throw new Error(`Failed to checkout selected skill source from ${source}#${ref}.${detail ? ` ${detail}` : ""}`);
|
|
46
50
|
}
|
|
47
|
-
|
|
51
|
+
moveCheckoutIntoCache(tempCheckout, checkout);
|
|
48
52
|
return checkout;
|
|
49
53
|
}
|
|
50
54
|
|
|
@@ -99,10 +103,44 @@ async function ensureSkillSourceRootAsync(manifest, options = {}) {
|
|
|
99
103
|
const detail = gitDetail(checkoutResult);
|
|
100
104
|
throw new Error(`Failed to checkout selected skill source from ${source}#${ref}.${detail ? ` ${detail}` : ""}`);
|
|
101
105
|
}
|
|
102
|
-
|
|
106
|
+
await moveCheckoutIntoCacheAsync(tempCheckout, checkout);
|
|
103
107
|
return checkout;
|
|
104
108
|
}
|
|
105
109
|
|
|
110
|
+
function moveCheckoutIntoCache(tempCheckout, checkout, options = {}) {
|
|
111
|
+
const renameSync = options.renameSync || fs.renameSync;
|
|
112
|
+
const retries = options.retries ?? DEFAULT_MOVE_RETRIES;
|
|
113
|
+
const delayMs = options.delayMs ?? DEFAULT_MOVE_DELAY_MS;
|
|
114
|
+
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
|
115
|
+
try {
|
|
116
|
+
renameSync(tempCheckout, checkout);
|
|
117
|
+
return;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (!isRetryableMoveError(error) || attempt === retries) {
|
|
120
|
+
throw cacheMoveError(error, tempCheckout, checkout);
|
|
121
|
+
}
|
|
122
|
+
sleepSync(delayMs * (attempt + 1));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function moveCheckoutIntoCacheAsync(tempCheckout, checkout, options = {}) {
|
|
128
|
+
const renameSync = options.renameSync || fs.renameSync;
|
|
129
|
+
const retries = options.retries ?? DEFAULT_MOVE_RETRIES;
|
|
130
|
+
const delayMs = options.delayMs ?? DEFAULT_MOVE_DELAY_MS;
|
|
131
|
+
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
|
132
|
+
try {
|
|
133
|
+
renameSync(tempCheckout, checkout);
|
|
134
|
+
return;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (!isRetryableMoveError(error) || attempt === retries) {
|
|
137
|
+
throw cacheMoveError(error, tempCheckout, checkout);
|
|
138
|
+
}
|
|
139
|
+
await sleep(delayMs * (attempt + 1));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
106
144
|
function requireDirectory(value, label) {
|
|
107
145
|
const dir = path.resolve(value);
|
|
108
146
|
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
|
@@ -163,7 +201,32 @@ function gitDetail(result) {
|
|
|
163
201
|
return (result.stderr || result.stdout || "").trim();
|
|
164
202
|
}
|
|
165
203
|
|
|
204
|
+
function isRetryableMoveError(error) {
|
|
205
|
+
return RETRYABLE_MOVE_CODES.has(error && error.code);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function cacheMoveError(error, tempCheckout, checkout) {
|
|
209
|
+
const wrapped = new Error(
|
|
210
|
+
`Failed to finalize skill source cache because the checkout directory is busy or locked. ` +
|
|
211
|
+
`Close terminals/editors using the cache and retry. ${tempCheckout} -> ${checkout}. ${error.message}`,
|
|
212
|
+
);
|
|
213
|
+
wrapped.code = error.code;
|
|
214
|
+
wrapped.cause = error;
|
|
215
|
+
return wrapped;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function sleep(ms) {
|
|
219
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function sleepSync(ms) {
|
|
223
|
+
if (ms <= 0) return;
|
|
224
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
225
|
+
}
|
|
226
|
+
|
|
166
227
|
module.exports = {
|
|
167
228
|
ensureSkillSourceRoot,
|
|
168
229
|
ensureSkillSourceRootAsync,
|
|
230
|
+
moveCheckoutIntoCache,
|
|
231
|
+
moveCheckoutIntoCacheAsync,
|
|
169
232
|
};
|