lua-cli 3.15.1 → 3.15.3
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/api/skills.api.service.d.ts +8 -0
- package/dist/api/skills.api.service.d.ts.map +1 -1
- package/dist/api/skills.api.service.js +8 -0
- package/dist/api/skills.api.service.js.map +1 -1
- package/dist/cli/command-definitions.d.ts.map +1 -1
- package/dist/cli/command-definitions.js +8 -0
- package/dist/cli/command-definitions.js.map +1 -1
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +13 -6
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/push.d.ts +12 -0
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +125 -8
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/source.d.ts.map +1 -1
- package/dist/commands/source.js +17 -7
- package/dist/commands/source.js.map +1 -1
- package/dist/compiler/plugins/agent.plugin.d.ts.map +1 -1
- package/dist/compiler/plugins/agent.plugin.js +2 -0
- package/dist/compiler/plugins/agent.plugin.js.map +1 -1
- package/dist/compiler/utils/common.d.ts +3 -19
- package/dist/compiler/utils/common.d.ts.map +1 -1
- package/dist/compiler/utils/common.js +8 -47
- package/dist/compiler/utils/common.js.map +1 -1
- package/dist/interfaces/backup.d.ts +6 -132
- package/dist/interfaces/backup.d.ts.map +1 -1
- package/dist/interfaces/backup.js +3 -4
- package/dist/interfaces/backup.js.map +1 -1
- package/dist/utils/backup-helpers.d.ts +6 -58
- package/dist/utils/backup-helpers.d.ts.map +1 -1
- package/dist/utils/backup-helpers.js +35 -158
- package/dist/utils/backup-helpers.js.map +1 -1
- package/dist/utils/build-manifest-from-disk.d.ts +3 -12
- package/dist/utils/build-manifest-from-disk.d.ts.map +1 -1
- package/dist/utils/build-manifest-from-disk.js +10 -100
- package/dist/utils/build-manifest-from-disk.js.map +1 -1
- package/node_modules/@lua/shared-source-sync/dist/index.d.mts +424 -0
- package/node_modules/@lua/shared-source-sync/dist/index.d.ts +424 -0
- package/node_modules/@lua/shared-source-sync/dist/index.js +536 -0
- package/node_modules/@lua/shared-source-sync/dist/index.mjs +491 -0
- package/node_modules/@lua/shared-source-sync/package.json +26 -0
- package/node_modules/@lua/shared-source-sync/src/archive.ts +98 -0
- package/node_modules/@lua/shared-source-sync/src/filter.ts +72 -0
- package/node_modules/@lua/shared-source-sync/src/hash.ts +40 -0
- package/node_modules/@lua/shared-source-sync/src/http.ts +132 -0
- package/node_modules/@lua/shared-source-sync/src/index.ts +56 -0
- package/node_modules/@lua/shared-source-sync/src/pull.ts +50 -0
- package/node_modules/@lua/shared-source-sync/src/push.ts +94 -0
- package/node_modules/@lua/shared-source-sync/src/restore.ts +83 -0
- package/node_modules/@lua/shared-source-sync/src/skill-source.ts +55 -0
- package/node_modules/@lua/shared-source-sync/src/transfer.ts +92 -0
- package/node_modules/@lua/shared-source-sync/src/types.ts +88 -0
- package/node_modules/@lua/shared-source-sync/src/walk.ts +91 -0
- package/node_modules/@lua/shared-source-sync/tests/archive.test.ts +108 -0
- package/node_modules/@lua/shared-source-sync/tests/hash.test.ts +40 -0
- package/node_modules/@lua/shared-source-sync/tests/http.test.ts +77 -0
- package/node_modules/@lua/shared-source-sync/tests/push.test.ts +162 -0
- package/node_modules/@lua/shared-source-sync/tests/walk.test.ts +84 -0
- package/node_modules/@lua/shared-source-sync/tsconfig.json +12 -0
- package/node_modules/@lua/shared-source-sync/tsup.config.ts +9 -0
- package/node_modules/@lua/shared-source-sync/vitest.config.ts +8 -0
- package/node_modules/@lua/shared-types/dist/index.d.mts +27 -0
- package/node_modules/@lua/shared-types/dist/index.d.ts +27 -0
- package/node_modules/@lua/shared-types/src/agent-config.types.ts +14 -0
- package/node_modules/@lua/shared-types/src/chat-stream.types.ts +7 -0
- package/node_modules/@lua/shared-types/src/model-registry.ts +6 -0
- package/package.json +3 -1
- package/template/package.json +1 -1
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ARCHIVE_SCHEMA_VERSION: () => ARCHIVE_SCHEMA_VERSION,
|
|
24
|
+
BackupHttpClient: () => BackupHttpClient,
|
|
25
|
+
BackupHttpError: () => BackupHttpError,
|
|
26
|
+
DEFAULT_MAX_FILE_BYTES: () => DEFAULT_MAX_FILE_BYTES,
|
|
27
|
+
FILE_HASH_LENGTH: () => FILE_HASH_LENGTH,
|
|
28
|
+
SKILL_SOURCE_EDITORS: () => SKILL_SOURCE_EDITORS,
|
|
29
|
+
classifyFile: () => classifyFile,
|
|
30
|
+
combineFileHashes: () => combineFileHashes,
|
|
31
|
+
decodeWorkspaceArchive: () => decodeWorkspaceArchive,
|
|
32
|
+
downloadBlobs: () => downloadBlobs,
|
|
33
|
+
encodeWorkspaceArchive: () => encodeWorkspaceArchive,
|
|
34
|
+
hashContentTruncated: () => hashContentTruncated,
|
|
35
|
+
pullAgentBackup: () => pullAgentBackup,
|
|
36
|
+
pushAgentBackup: () => pushAgentBackup,
|
|
37
|
+
resolveBackupFileTarget: () => resolveBackupFileTarget,
|
|
38
|
+
restoreFromBlobs: () => restoreFromBlobs,
|
|
39
|
+
sha256Hex: () => sha256Hex,
|
|
40
|
+
shouldSkipDirectory: () => shouldSkipDirectory,
|
|
41
|
+
shouldSkipFile: () => shouldSkipFile,
|
|
42
|
+
uploadBlobs: () => uploadBlobs,
|
|
43
|
+
walkWorkspace: () => walkWorkspace,
|
|
44
|
+
writeArchiveToWorkspace: () => writeArchiveToWorkspace
|
|
45
|
+
});
|
|
46
|
+
module.exports = __toCommonJS(index_exports);
|
|
47
|
+
|
|
48
|
+
// src/hash.ts
|
|
49
|
+
var import_crypto = require("crypto");
|
|
50
|
+
var FILE_HASH_LENGTH = 16;
|
|
51
|
+
function hashContentTruncated(content) {
|
|
52
|
+
return (0, import_crypto.createHash)("sha256").update(content).digest("hex").slice(0, FILE_HASH_LENGTH);
|
|
53
|
+
}
|
|
54
|
+
__name(hashContentTruncated, "hashContentTruncated");
|
|
55
|
+
function sha256Hex(content) {
|
|
56
|
+
return (0, import_crypto.createHash)("sha256").update(content).digest("hex");
|
|
57
|
+
}
|
|
58
|
+
__name(sha256Hex, "sha256Hex");
|
|
59
|
+
function combineFileHashes(files) {
|
|
60
|
+
const sorted = [
|
|
61
|
+
...files
|
|
62
|
+
].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
63
|
+
return sha256Hex(sorted.map((f) => f.hash).join("|"));
|
|
64
|
+
}
|
|
65
|
+
__name(combineFileHashes, "combineFileHashes");
|
|
66
|
+
|
|
67
|
+
// src/filter.ts
|
|
68
|
+
var import_path = require("path");
|
|
69
|
+
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
70
|
+
"node_modules",
|
|
71
|
+
"dist",
|
|
72
|
+
"dist-v2",
|
|
73
|
+
".git",
|
|
74
|
+
".lua",
|
|
75
|
+
".temp",
|
|
76
|
+
"coverage",
|
|
77
|
+
".next",
|
|
78
|
+
".turbo"
|
|
79
|
+
]);
|
|
80
|
+
var ARCHIVE_ONLY_SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
81
|
+
"build"
|
|
82
|
+
]);
|
|
83
|
+
var DEFAULT_MAX_FILE_BYTES = 256 * 1024;
|
|
84
|
+
function shouldSkipDirectory(name) {
|
|
85
|
+
return SKIP_DIRECTORIES.has(name);
|
|
86
|
+
}
|
|
87
|
+
__name(shouldSkipDirectory, "shouldSkipDirectory");
|
|
88
|
+
function shouldSkipFile(filePathOrName) {
|
|
89
|
+
return filePathOrName.includes("node_modules") || filePathOrName.includes(".test.") || filePathOrName.includes(".spec.") || filePathOrName.includes("__tests__") || filePathOrName.endsWith(".d.ts") || filePathOrName.endsWith(".map") || filePathOrName.startsWith(".") || filePathOrName === "package-lock.json" || filePathOrName === "yarn.lock" || filePathOrName === "pnpm-lock.yaml";
|
|
90
|
+
}
|
|
91
|
+
__name(shouldSkipFile, "shouldSkipFile");
|
|
92
|
+
var KIND_BY_EXT = {
|
|
93
|
+
".ts": "source",
|
|
94
|
+
".tsx": "source",
|
|
95
|
+
".js": "source",
|
|
96
|
+
".jsx": "source",
|
|
97
|
+
".yaml": "config",
|
|
98
|
+
".yml": "config",
|
|
99
|
+
".json": "config",
|
|
100
|
+
".toml": "config"
|
|
101
|
+
};
|
|
102
|
+
function classifyFile(relPath) {
|
|
103
|
+
return KIND_BY_EXT[(0, import_path.extname)(relPath)] ?? "other";
|
|
104
|
+
}
|
|
105
|
+
__name(classifyFile, "classifyFile");
|
|
106
|
+
|
|
107
|
+
// src/walk.ts
|
|
108
|
+
var import_fs = require("fs");
|
|
109
|
+
var import_path2 = require("path");
|
|
110
|
+
function walkWorkspace(rootDir, opts = {}) {
|
|
111
|
+
const maxBytes = opts.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
112
|
+
const refs = [];
|
|
113
|
+
const contentByHash = /* @__PURE__ */ new Map();
|
|
114
|
+
let totalSize = 0;
|
|
115
|
+
const visit = /* @__PURE__ */ __name((relPrefix) => {
|
|
116
|
+
const absDir = relPrefix ? (0, import_path2.join)(rootDir, relPrefix) : rootDir;
|
|
117
|
+
let entries;
|
|
118
|
+
try {
|
|
119
|
+
entries = (0, import_fs.readdirSync)(absDir, {
|
|
120
|
+
withFileTypes: true
|
|
121
|
+
});
|
|
122
|
+
} catch {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
for (const entry of entries) {
|
|
126
|
+
if (entry.isDirectory()) {
|
|
127
|
+
if (shouldSkipDirectory(entry.name)) continue;
|
|
128
|
+
if (shouldSkipFile(entry.name)) continue;
|
|
129
|
+
visit(relPrefix ? `${relPrefix}${import_path2.sep}${entry.name}` : entry.name);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (!entry.isFile()) continue;
|
|
133
|
+
if (shouldSkipFile(entry.name)) continue;
|
|
134
|
+
const rel = (relPrefix ? `${relPrefix}${import_path2.sep}${entry.name}` : entry.name).split(import_path2.sep).join("/");
|
|
135
|
+
const abs = (0, import_path2.join)(rootDir, rel);
|
|
136
|
+
let stats;
|
|
137
|
+
try {
|
|
138
|
+
stats = (0, import_fs.statSync)(abs);
|
|
139
|
+
} catch {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (stats.size > maxBytes) continue;
|
|
143
|
+
let content;
|
|
144
|
+
try {
|
|
145
|
+
content = (0, import_fs.readFileSync)(abs);
|
|
146
|
+
} catch {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const hash = hashContentTruncated(content.toString("utf-8"));
|
|
150
|
+
refs.push({
|
|
151
|
+
relativePath: rel,
|
|
152
|
+
hash,
|
|
153
|
+
size: stats.size,
|
|
154
|
+
type: classifyFile(rel)
|
|
155
|
+
});
|
|
156
|
+
if (!contentByHash.has(hash)) contentByHash.set(hash, content);
|
|
157
|
+
totalSize += stats.size;
|
|
158
|
+
}
|
|
159
|
+
}, "visit");
|
|
160
|
+
visit("");
|
|
161
|
+
refs.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
162
|
+
const projectHash = combineFileHashes(refs);
|
|
163
|
+
return {
|
|
164
|
+
files: refs,
|
|
165
|
+
projectHash,
|
|
166
|
+
totalSize,
|
|
167
|
+
contentByHash
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
__name(walkWorkspace, "walkWorkspace");
|
|
171
|
+
|
|
172
|
+
// src/http.ts
|
|
173
|
+
var BackupHttpError = class extends Error {
|
|
174
|
+
static {
|
|
175
|
+
__name(this, "BackupHttpError");
|
|
176
|
+
}
|
|
177
|
+
status;
|
|
178
|
+
endpoint;
|
|
179
|
+
body;
|
|
180
|
+
constructor(message, status, endpoint, body) {
|
|
181
|
+
super(message), this.status = status, this.endpoint = endpoint, this.body = body;
|
|
182
|
+
this.name = "BackupHttpError";
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
var BackupHttpClient = class {
|
|
186
|
+
static {
|
|
187
|
+
__name(this, "BackupHttpClient");
|
|
188
|
+
}
|
|
189
|
+
options;
|
|
190
|
+
fetchFn;
|
|
191
|
+
constructor(options) {
|
|
192
|
+
this.options = options;
|
|
193
|
+
this.fetchFn = options.fetch ?? fetch;
|
|
194
|
+
}
|
|
195
|
+
url(path) {
|
|
196
|
+
const base = this.options.baseUrl.replace(/\/$/, "");
|
|
197
|
+
return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path}`;
|
|
198
|
+
}
|
|
199
|
+
async json(method, path, body) {
|
|
200
|
+
const endpoint = this.url(path);
|
|
201
|
+
const res = await this.fetchFn(endpoint, {
|
|
202
|
+
method,
|
|
203
|
+
headers: {
|
|
204
|
+
Authorization: this.options.authHeader,
|
|
205
|
+
...body !== void 0 ? {
|
|
206
|
+
"Content-Type": "application/json"
|
|
207
|
+
} : {},
|
|
208
|
+
Accept: "application/json"
|
|
209
|
+
},
|
|
210
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
211
|
+
});
|
|
212
|
+
if (!res.ok) {
|
|
213
|
+
let text2;
|
|
214
|
+
try {
|
|
215
|
+
text2 = await res.text();
|
|
216
|
+
} catch {
|
|
217
|
+
text2 = void 0;
|
|
218
|
+
}
|
|
219
|
+
throw new BackupHttpError(`Backup request failed: ${method} ${path} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
|
|
220
|
+
}
|
|
221
|
+
const text = await res.text();
|
|
222
|
+
if (!text) return void 0;
|
|
223
|
+
return JSON.parse(text);
|
|
224
|
+
}
|
|
225
|
+
/** `POST /backup/check-blobs` — returns existing vs missing partition. */
|
|
226
|
+
checkBlobsExist(hashes) {
|
|
227
|
+
return this.json("POST", "/backup/check-blobs", {
|
|
228
|
+
hashes
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
/** `POST /backup/blob-upload-urls` — presigned S3 PUT URLs. */
|
|
232
|
+
getBlobUploadUrls(hashes) {
|
|
233
|
+
return this.json("POST", "/backup/blob-upload-urls", {
|
|
234
|
+
hashes
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/** `POST /backup/blob-urls` — presigned S3 GET URLs (for restore). */
|
|
238
|
+
getBlobUrls(hashes) {
|
|
239
|
+
return this.json("POST", "/backup/blob-urls", {
|
|
240
|
+
hashes
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/** `POST /backup/manifest` — final step in a push; returns server metadata. */
|
|
244
|
+
saveManifest(data) {
|
|
245
|
+
return this.json("POST", "/backup/manifest", data);
|
|
246
|
+
}
|
|
247
|
+
/** `GET /backup` — metadata only, no file list. */
|
|
248
|
+
getMetadata() {
|
|
249
|
+
return this.json("GET", "/backup");
|
|
250
|
+
}
|
|
251
|
+
/** `GET /backup/manifest` — metadata + file list. */
|
|
252
|
+
getManifest() {
|
|
253
|
+
return this.json("GET", "/backup/manifest");
|
|
254
|
+
}
|
|
255
|
+
/** `GET /backup/check/:hash` — fast freshness probe. */
|
|
256
|
+
checkBackupExists(hash) {
|
|
257
|
+
return this.json("GET", `/backup/check/${encodeURIComponent(hash)}`);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/transfer.ts
|
|
262
|
+
var import_zlib = require("zlib");
|
|
263
|
+
var DEFAULT_CONCURRENCY = 10;
|
|
264
|
+
async function uploadBlobs(opts) {
|
|
265
|
+
const fetchFn = opts.fetch ?? fetch;
|
|
266
|
+
const concurrency = opts.concurrency ?? DEFAULT_CONCURRENCY;
|
|
267
|
+
const hashes = Object.keys(opts.uploadUrls);
|
|
268
|
+
for (let i = 0; i < hashes.length; i += concurrency) {
|
|
269
|
+
const batch = hashes.slice(i, i + concurrency);
|
|
270
|
+
await Promise.all(batch.map(async (hash) => {
|
|
271
|
+
const buf = opts.contentByHash.get(hash);
|
|
272
|
+
if (!buf) {
|
|
273
|
+
throw new Error(`uploadBlobs: no content for hash ${hash.slice(0, 12)}\u2026`);
|
|
274
|
+
}
|
|
275
|
+
const compressed = (0, import_zlib.gzipSync)(buf);
|
|
276
|
+
const res = await fetchFn(opts.uploadUrls[hash], {
|
|
277
|
+
method: "PUT",
|
|
278
|
+
body: compressed,
|
|
279
|
+
headers: {
|
|
280
|
+
"Content-Type": "application/octet-stream"
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
if (!res.ok) {
|
|
284
|
+
throw new Error(`S3 upload failed for ${hash.slice(0, 12)}\u2026: ${res.status} ${res.statusText}`);
|
|
285
|
+
}
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
return hashes.length;
|
|
289
|
+
}
|
|
290
|
+
__name(uploadBlobs, "uploadBlobs");
|
|
291
|
+
async function downloadBlobs(opts) {
|
|
292
|
+
const fetchFn = opts.fetch ?? fetch;
|
|
293
|
+
const concurrency = opts.concurrency ?? DEFAULT_CONCURRENCY;
|
|
294
|
+
const out = /* @__PURE__ */ new Map();
|
|
295
|
+
const entries = Object.entries(opts.urls);
|
|
296
|
+
for (let i = 0; i < entries.length; i += concurrency) {
|
|
297
|
+
const batch = entries.slice(i, i + concurrency);
|
|
298
|
+
const downloaded = await Promise.all(batch.map(async ([hash, url]) => {
|
|
299
|
+
const res = await fetchFn(url);
|
|
300
|
+
if (!res.ok) {
|
|
301
|
+
throw new Error(`S3 download failed for ${hash.slice(0, 12)}\u2026: ${res.status} ${res.statusText}`);
|
|
302
|
+
}
|
|
303
|
+
const ab = await res.arrayBuffer();
|
|
304
|
+
const buf = Buffer.from(ab);
|
|
305
|
+
if (buf.length >= 2 && buf[0] === 31 && buf[1] === 139) {
|
|
306
|
+
try {
|
|
307
|
+
return [
|
|
308
|
+
hash,
|
|
309
|
+
(0, import_zlib.gunzipSync)(buf)
|
|
310
|
+
];
|
|
311
|
+
} catch {
|
|
312
|
+
return [
|
|
313
|
+
hash,
|
|
314
|
+
buf
|
|
315
|
+
];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return [
|
|
319
|
+
hash,
|
|
320
|
+
buf
|
|
321
|
+
];
|
|
322
|
+
}));
|
|
323
|
+
for (const [hash, buf] of downloaded) out.set(hash, buf);
|
|
324
|
+
}
|
|
325
|
+
return out;
|
|
326
|
+
}
|
|
327
|
+
__name(downloadBlobs, "downloadBlobs");
|
|
328
|
+
|
|
329
|
+
// src/restore.ts
|
|
330
|
+
var import_fs2 = require("fs");
|
|
331
|
+
var import_path3 = require("path");
|
|
332
|
+
function resolveBackupFileTarget(file, targetDir) {
|
|
333
|
+
const base = (0, import_path3.resolve)(targetDir);
|
|
334
|
+
const sandboxed = file.external ? (0, import_path3.join)(base, ".lua", "external", file.relativePath) : (0, import_path3.join)(base, file.relativePath);
|
|
335
|
+
const resolved = (0, import_path3.resolve)(sandboxed);
|
|
336
|
+
if (resolved !== base && !resolved.startsWith(base + import_path3.sep)) {
|
|
337
|
+
throw new Error(`Backup entry escapes target directory: ${file.relativePath}`);
|
|
338
|
+
}
|
|
339
|
+
return resolved;
|
|
340
|
+
}
|
|
341
|
+
__name(resolveBackupFileTarget, "resolveBackupFileTarget");
|
|
342
|
+
function restoreFromBlobs(manifest, blobs, targetDir, opts = {}) {
|
|
343
|
+
let filesWritten = 0;
|
|
344
|
+
let filesUnchanged = 0;
|
|
345
|
+
let filesSkipped = 0;
|
|
346
|
+
for (const file of manifest.files) {
|
|
347
|
+
const content = blobs.get(file.hash);
|
|
348
|
+
if (!content) {
|
|
349
|
+
throw new Error(`Blob not found for hash: ${file.hash} (${file.relativePath})`);
|
|
350
|
+
}
|
|
351
|
+
const targetPath = resolveBackupFileTarget(file, targetDir);
|
|
352
|
+
if ((0, import_fs2.existsSync)(targetPath)) {
|
|
353
|
+
const sameSize = (0, import_fs2.statSync)(targetPath).size === content.length;
|
|
354
|
+
if (sameSize && (0, import_fs2.readFileSync)(targetPath).equals(content)) {
|
|
355
|
+
filesUnchanged++;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (!opts.overwrite) {
|
|
359
|
+
filesSkipped++;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
(0, import_fs2.mkdirSync)((0, import_path3.dirname)(targetPath), {
|
|
364
|
+
recursive: true
|
|
365
|
+
});
|
|
366
|
+
(0, import_fs2.writeFileSync)(targetPath, content);
|
|
367
|
+
filesWritten++;
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
filesWritten,
|
|
371
|
+
filesUnchanged,
|
|
372
|
+
filesSkipped
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
__name(restoreFromBlobs, "restoreFromBlobs");
|
|
376
|
+
|
|
377
|
+
// src/push.ts
|
|
378
|
+
async function pushAgentBackup(opts) {
|
|
379
|
+
const snapshot = walkWorkspace(opts.workspaceDir, opts.walkOptions);
|
|
380
|
+
const client = new BackupHttpClient(opts.http);
|
|
381
|
+
const uniqueHashes = [
|
|
382
|
+
...new Set(snapshot.files.map((f) => f.hash))
|
|
383
|
+
];
|
|
384
|
+
let filesUploaded = 0;
|
|
385
|
+
if (uniqueHashes.length > 0) {
|
|
386
|
+
const checked = await client.checkBlobsExist(uniqueHashes);
|
|
387
|
+
if (checked.missing.length > 0) {
|
|
388
|
+
const urls = await client.getBlobUploadUrls(checked.missing);
|
|
389
|
+
filesUploaded = await uploadBlobs({
|
|
390
|
+
uploadUrls: urls.urls,
|
|
391
|
+
contentByHash: snapshot.contentByHash,
|
|
392
|
+
fetch: opts.http.fetch,
|
|
393
|
+
concurrency: opts.concurrency
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
const metadata = await client.saveManifest({
|
|
398
|
+
projectHash: snapshot.projectHash,
|
|
399
|
+
files: snapshot.files,
|
|
400
|
+
version: opts.version,
|
|
401
|
+
orgId: opts.orgId,
|
|
402
|
+
createdBy: opts.createdBy ?? "cli",
|
|
403
|
+
triggeredBy: opts.triggeredBy
|
|
404
|
+
});
|
|
405
|
+
return {
|
|
406
|
+
projectHash: snapshot.projectHash,
|
|
407
|
+
fileCount: snapshot.files.length,
|
|
408
|
+
filesUploaded,
|
|
409
|
+
activeVersion: metadata.activeVersion,
|
|
410
|
+
metadata,
|
|
411
|
+
files: snapshot.files
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
__name(pushAgentBackup, "pushAgentBackup");
|
|
415
|
+
|
|
416
|
+
// src/pull.ts
|
|
417
|
+
async function pullAgentBackup(opts) {
|
|
418
|
+
const client = new BackupHttpClient(opts.http);
|
|
419
|
+
const manifest = await client.getManifest();
|
|
420
|
+
const uniqueHashes = [
|
|
421
|
+
...new Set(manifest.files.map((f) => f.hash))
|
|
422
|
+
];
|
|
423
|
+
const blobs = uniqueHashes.length ? await (async () => {
|
|
424
|
+
const urls = await client.getBlobUrls(uniqueHashes);
|
|
425
|
+
return downloadBlobs({
|
|
426
|
+
urls: urls.urls,
|
|
427
|
+
fetch: opts.http.fetch,
|
|
428
|
+
concurrency: opts.concurrency
|
|
429
|
+
});
|
|
430
|
+
})() : /* @__PURE__ */ new Map();
|
|
431
|
+
const result = restoreFromBlobs(manifest, blobs, opts.targetDir, opts.restore);
|
|
432
|
+
return {
|
|
433
|
+
...result,
|
|
434
|
+
manifest
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
__name(pullAgentBackup, "pullAgentBackup");
|
|
438
|
+
|
|
439
|
+
// src/archive.ts
|
|
440
|
+
var import_fs3 = require("fs");
|
|
441
|
+
var import_path4 = require("path");
|
|
442
|
+
var import_zlib2 = require("zlib");
|
|
443
|
+
var ARCHIVE_SCHEMA_VERSION = 1;
|
|
444
|
+
function encodeWorkspaceArchive(workspaceDir) {
|
|
445
|
+
const files = {};
|
|
446
|
+
try {
|
|
447
|
+
walk(workspaceDir, "", files);
|
|
448
|
+
} catch {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
if (Object.keys(files).length === 0) return null;
|
|
452
|
+
const gz = (0, import_zlib2.gzipSync)(Buffer.from(JSON.stringify(files), "utf8"));
|
|
453
|
+
return gz.toString("base64");
|
|
454
|
+
}
|
|
455
|
+
__name(encodeWorkspaceArchive, "encodeWorkspaceArchive");
|
|
456
|
+
function decodeWorkspaceArchive(archive, schemaVersion) {
|
|
457
|
+
if (schemaVersion !== void 0 && schemaVersion !== ARCHIVE_SCHEMA_VERSION) {
|
|
458
|
+
throw new Error(`Unsupported archive schema version: ${schemaVersion} (expected ${ARCHIVE_SCHEMA_VERSION})`);
|
|
459
|
+
}
|
|
460
|
+
const raw = (0, import_zlib2.gunzipSync)(Buffer.from(archive, "base64")).toString("utf8");
|
|
461
|
+
const parsed = JSON.parse(raw);
|
|
462
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
463
|
+
throw new Error("Decoded archive is not a JSON object map");
|
|
464
|
+
}
|
|
465
|
+
return parsed;
|
|
466
|
+
}
|
|
467
|
+
__name(decodeWorkspaceArchive, "decodeWorkspaceArchive");
|
|
468
|
+
function writeArchiveToWorkspace(workspaceDir, files) {
|
|
469
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
470
|
+
if (relPath.includes("..")) {
|
|
471
|
+
throw new Error(`Refusing to write archived path containing '..': ${relPath}`);
|
|
472
|
+
}
|
|
473
|
+
const abs = (0, import_path4.join)(workspaceDir, relPath);
|
|
474
|
+
(0, import_fs3.mkdirSync)((0, import_path4.dirname)(abs), {
|
|
475
|
+
recursive: true
|
|
476
|
+
});
|
|
477
|
+
(0, import_fs3.writeFileSync)(abs, content, "utf8");
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
__name(writeArchiveToWorkspace, "writeArchiveToWorkspace");
|
|
481
|
+
function walk(root, prefix, out) {
|
|
482
|
+
const dir = prefix ? (0, import_path4.join)(root, prefix) : root;
|
|
483
|
+
const entries = (0, import_fs3.readdirSync)(dir, {
|
|
484
|
+
withFileTypes: true
|
|
485
|
+
});
|
|
486
|
+
for (const entry of entries) {
|
|
487
|
+
if (entry.isDirectory()) {
|
|
488
|
+
if (shouldSkipDirectory(entry.name) || ARCHIVE_ONLY_SKIP_DIRECTORIES.has(entry.name) || shouldSkipFile(entry.name)) continue;
|
|
489
|
+
walk(root, prefix ? `${prefix}${import_path4.sep}${entry.name}` : entry.name, out);
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
if (!entry.isFile()) continue;
|
|
493
|
+
if (shouldSkipFile(entry.name)) continue;
|
|
494
|
+
const relPath = (prefix ? `${prefix}${import_path4.sep}${entry.name}` : entry.name).split(import_path4.sep).join("/");
|
|
495
|
+
const abs = (0, import_path4.join)(dir, entry.name);
|
|
496
|
+
if ((0, import_fs3.statSync)(abs).size > DEFAULT_MAX_FILE_BYTES) continue;
|
|
497
|
+
try {
|
|
498
|
+
out[relPath] = (0, import_fs3.readFileSync)(abs, "utf8");
|
|
499
|
+
} catch {
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
__name(walk, "walk");
|
|
504
|
+
|
|
505
|
+
// src/skill-source.ts
|
|
506
|
+
var SKILL_SOURCE_EDITORS = [
|
|
507
|
+
"builder",
|
|
508
|
+
"cli",
|
|
509
|
+
"engineer",
|
|
510
|
+
"system"
|
|
511
|
+
];
|
|
512
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
513
|
+
0 && (module.exports = {
|
|
514
|
+
ARCHIVE_SCHEMA_VERSION,
|
|
515
|
+
BackupHttpClient,
|
|
516
|
+
BackupHttpError,
|
|
517
|
+
DEFAULT_MAX_FILE_BYTES,
|
|
518
|
+
FILE_HASH_LENGTH,
|
|
519
|
+
SKILL_SOURCE_EDITORS,
|
|
520
|
+
classifyFile,
|
|
521
|
+
combineFileHashes,
|
|
522
|
+
decodeWorkspaceArchive,
|
|
523
|
+
downloadBlobs,
|
|
524
|
+
encodeWorkspaceArchive,
|
|
525
|
+
hashContentTruncated,
|
|
526
|
+
pullAgentBackup,
|
|
527
|
+
pushAgentBackup,
|
|
528
|
+
resolveBackupFileTarget,
|
|
529
|
+
restoreFromBlobs,
|
|
530
|
+
sha256Hex,
|
|
531
|
+
shouldSkipDirectory,
|
|
532
|
+
shouldSkipFile,
|
|
533
|
+
uploadBlobs,
|
|
534
|
+
walkWorkspace,
|
|
535
|
+
writeArchiveToWorkspace
|
|
536
|
+
});
|