dorky 4.1.3 → 4.1.4

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.
Files changed (3) hide show
  1. package/bin/index.js +50 -31
  2. package/bin/mcp.js +50 -31
  3. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -23,6 +23,23 @@ const SCOPES = ['https://www.googleapis.com/auth/drive'];
23
23
  // Helpers
24
24
  const readJson = (p) => existsSync(p) ? JSON.parse(readFileSync(p)) : {};
25
25
  const writeJson = (p, d) => writeFileSync(p, JSON.stringify(d, null, 2));
26
+ const toPosix = (p) => p ? p.replace(/\\/g, '/') : p;
27
+ const normalizeKeys = (obj) => {
28
+ if (!obj) return {};
29
+ const out = {};
30
+ for (const k of Object.keys(obj)) out[toPosix(k)] = obj[k];
31
+ return out;
32
+ };
33
+ const readMetadata = () => {
34
+ const meta = readJson(METADATA_PATH);
35
+ meta["stage-1-files"] = normalizeKeys(meta["stage-1-files"]);
36
+ meta["uploaded-files"] = normalizeKeys(meta["uploaded-files"]);
37
+ return meta;
38
+ };
39
+ const readHistory = () => {
40
+ const history = existsSync(HISTORY_PATH) ? JSON.parse(readFileSync(HISTORY_PATH)) : [];
41
+ return history.map(e => ({ ...e, files: normalizeKeys(e.files) }));
42
+ };
26
43
 
27
44
  const checkDorkyProject = () => {
28
45
  if (!existsSync(DORKY_DIR) && !existsSync(".dorkyignore")) {
@@ -123,7 +140,7 @@ async function init(storage) {
123
140
 
124
141
  async function list(type) {
125
142
  checkDorkyProject();
126
- const meta = readJson(METADATA_PATH);
143
+ const meta = readMetadata();
127
144
  if (type === "remote") {
128
145
  if (!await checkCredentials()) return;
129
146
  const creds = readJson(CREDENTIALS_PATH);
@@ -157,7 +174,7 @@ async function list(type) {
157
174
  const files = await glob("**/*", { dot: true, ignore: [...exclusions.map(e => `**/${e}/**`), ...exclusions, ".dorky/**", ".dorkyignore", ".git/**", "node_modules/**"] });
158
175
 
159
176
  files.forEach(f => {
160
- const rel = path.relative(process.cwd(), f);
177
+ const rel = toPosix(path.relative(process.cwd(), f));
161
178
  if (rel.includes('.env') || rel.includes('.config')) console.log(chalk.yellow(` ⚠ ${rel} (Potential sensitive file)`));
162
179
  else console.log(chalk.gray(` ${rel}`));
163
180
  });
@@ -168,14 +185,15 @@ async function list(type) {
168
185
 
169
186
  function add(files) {
170
187
  checkDorkyProject();
171
- const meta = readJson(METADATA_PATH);
188
+ const meta = readMetadata();
172
189
  const added = [];
173
190
  files.forEach(f => {
174
191
  if (!existsSync(f)) return console.log(chalk.red(`✖ File not found: ${f}`));
175
192
  const hash = md5(readFileSync(f));
176
- if (meta["stage-1-files"][f]?.hash === hash) return console.log(chalk.gray(`• ${f} (unchanged)`));
177
- meta["stage-1-files"][f] = { "mime-type": mimeTypes.lookup(f) || "application/octet-stream", hash };
178
- added.push(f);
193
+ const key = toPosix(f);
194
+ if (meta["stage-1-files"][key]?.hash === hash) return console.log(chalk.gray(`• ${key} (unchanged)`));
195
+ meta["stage-1-files"][key] = { "mime-type": mimeTypes.lookup(f) || "application/octet-stream", hash };
196
+ added.push(key);
179
197
  });
180
198
  writeJson(METADATA_PATH, meta);
181
199
  added.forEach(f => console.log(chalk.green(`✔ Staged: ${f}`)));
@@ -183,10 +201,11 @@ function add(files) {
183
201
 
184
202
  function rm(files) {
185
203
  checkDorkyProject();
186
- const meta = readJson(METADATA_PATH);
204
+ const meta = readMetadata();
187
205
  const removed = files.filter(f => {
188
- if (!meta["stage-1-files"][f]) return false;
189
- delete meta["stage-1-files"][f];
206
+ const key = toPosix(f);
207
+ if (!meta["stage-1-files"][key]) return false;
208
+ delete meta["stage-1-files"][key];
190
209
  return true;
191
210
  });
192
211
  writeJson(METADATA_PATH, meta);
@@ -269,7 +288,7 @@ async function runDrive(fn) {
269
288
  async function push() {
270
289
  checkDorkyProject();
271
290
  if (!await checkCredentials()) return;
272
- const meta = readJson(METADATA_PATH);
291
+ const meta = readMetadata();
273
292
  const filesToUpload = Object.keys(meta["stage-1-files"])
274
293
  .filter(f => !meta["uploaded-files"][f] || meta["stage-1-files"][f].hash !== meta["uploaded-files"][f].hash)
275
294
  .map(f => ({ name: f, ...meta["stage-1-files"][f] }));
@@ -281,7 +300,7 @@ async function push() {
281
300
 
282
301
  const commitFiles = { ...meta["stage-1-files"] };
283
302
  const commitId = md5(JSON.stringify(commitFiles)).slice(0, 8);
284
- const history = existsSync(HISTORY_PATH) ? JSON.parse(readFileSync(HISTORY_PATH)) : [];
303
+ const history = readHistory();
285
304
  if (history.length > 0 && history[history.length - 1].id === commitId) return console.log(chalk.yellow("ℹ Already on the latest commit. Nothing to push."));
286
305
 
287
306
  const creds = readJson(CREDENTIALS_PATH);
@@ -289,14 +308,14 @@ async function push() {
289
308
  await runS3(creds, async (s3, bucket) => {
290
309
  if (filesToUpload.length > 0) {
291
310
  await Promise.all(filesToUpload.map(async f => {
292
- const key = path.join(path.basename(process.cwd()), f.name);
311
+ const key = path.posix.join(path.basename(process.cwd()), f.name);
293
312
  await s3.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: readFileSync(f.name) }));
294
313
  console.log(chalk.green(`✔ Uploaded: ${f.name}`));
295
314
  }));
296
315
  }
297
316
  if (filesToDelete.length > 0) {
298
317
  await Promise.all(filesToDelete.map(async f => {
299
- const key = path.join(path.basename(process.cwd()), f);
318
+ const key = path.posix.join(path.basename(process.cwd()), f);
300
319
  await s3.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
301
320
  console.log(chalk.yellow(`✔ Deleted remote: ${f}`));
302
321
  }));
@@ -307,9 +326,9 @@ async function push() {
307
326
  if (filesToUpload.length > 0) {
308
327
  for (const f of filesToUpload) {
309
328
  const root = path.basename(process.cwd());
310
- const parentId = await getFolderId(path.dirname(path.join(root, f.name)), drive);
329
+ const parentId = await getFolderId(path.posix.dirname(path.posix.join(root, f.name)), drive);
311
330
  await drive.files.create({
312
- requestBody: { name: path.basename(f.name), parents: [parentId] },
331
+ requestBody: { name: path.posix.basename(f.name), parents: [parentId] },
313
332
  media: { mimeType: f["mime-type"], body: createReadStream(f.name) }
314
333
  });
315
334
  console.log(chalk.green(`✔ Uploaded: ${f.name}`));
@@ -318,10 +337,10 @@ async function push() {
318
337
  if (filesToDelete.length > 0) {
319
338
  const root = path.basename(process.cwd());
320
339
  for (const f of filesToDelete) {
321
- const parentId = await getFolderId(path.dirname(path.join(root, f)), drive, false);
340
+ const parentId = await getFolderId(path.posix.dirname(path.posix.join(root, f)), drive, false);
322
341
  if (parentId) {
323
342
  const res = await drive.files.list({
324
- q: `name='${path.basename(f)}' and '${parentId}' in parents and trashed=false`,
343
+ q: `name='${path.posix.basename(f)}' and '${parentId}' in parents and trashed=false`,
325
344
  fields: 'files(id)'
326
345
  });
327
346
  if (res.data.files[0]) {
@@ -341,20 +360,20 @@ async function push() {
341
360
  writeJson(HISTORY_PATH, history);
342
361
 
343
362
  const root = path.basename(process.cwd());
344
- const historyPrefix = path.join(root, ".dorky-history", commitId);
363
+ const historyPrefix = path.posix.join(root, ".dorky-history", commitId);
345
364
  if (creds.storage === "aws") {
346
365
  await runS3(creds, async (s3, bucket) => {
347
366
  await Promise.all(Object.keys(commitFiles).map(async f => {
348
- const key = path.join(historyPrefix, f);
367
+ const key = path.posix.join(historyPrefix, f);
349
368
  await s3.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: readFileSync(f) }));
350
369
  }));
351
370
  });
352
371
  } else if (creds.storage === "google-drive") {
353
372
  await runDrive(async (drive) => {
354
373
  for (const f of Object.keys(commitFiles)) {
355
- const parentId = await getFolderId(path.join(root, ".dorky-history", commitId, path.dirname(f)), drive);
374
+ const parentId = await getFolderId(path.posix.join(root, ".dorky-history", commitId, path.posix.dirname(f)), drive);
356
375
  await drive.files.create({
357
- requestBody: { name: path.basename(f), parents: [parentId] },
376
+ requestBody: { name: path.posix.basename(f), parents: [parentId] },
358
377
  media: { mimeType: commitFiles[f]["mime-type"], body: createReadStream(f) }
359
378
  });
360
379
  }
@@ -366,14 +385,14 @@ async function push() {
366
385
  async function pull() {
367
386
  checkDorkyProject();
368
387
  if (!await checkCredentials()) return;
369
- const meta = readJson(METADATA_PATH);
388
+ const meta = readMetadata();
370
389
  const files = meta["uploaded-files"];
371
390
  const creds = readJson(CREDENTIALS_PATH);
372
391
 
373
392
  if (creds.storage === "aws") {
374
393
  await runS3(creds, async (s3, bucket) => {
375
394
  await Promise.all(Object.keys(files).map(async f => {
376
- const key = path.join(path.basename(process.cwd()), f);
395
+ const key = path.posix.join(path.basename(process.cwd()), f);
377
396
  const { Body } = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
378
397
  const dir = path.dirname(f);
379
398
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
@@ -385,7 +404,7 @@ async function pull() {
385
404
  await runDrive(async (drive) => {
386
405
  const fileList = Object.keys(files).map(k => ({ name: k, ...files[k] }));
387
406
  await Promise.all(fileList.map(async f => {
388
- const res = await drive.files.list({ q: `name='${path.basename(f.name)}' and mimeType!='application/vnd.google-apps.folder'`, fields: 'files(id)' });
407
+ const res = await drive.files.list({ q: `name='${path.posix.basename(f.name)}' and mimeType!='application/vnd.google-apps.folder'`, fields: 'files(id)' });
389
408
  if (!res.data.files[0]) return console.log(chalk.red(`✖ Missing remote file: ${f.name}`));
390
409
  const data = await drive.files.get({ fileId: res.data.files[0].id, alt: 'media' });
391
410
  if (!existsSync(path.dirname(f.name))) mkdirSync(path.dirname(f.name), { recursive: true });
@@ -398,7 +417,7 @@ async function pull() {
398
417
 
399
418
  function log() {
400
419
  checkDorkyProject();
401
- const history = existsSync(HISTORY_PATH) ? JSON.parse(readFileSync(HISTORY_PATH)) : [];
420
+ const history = readHistory();
402
421
  if (!history.length) return console.log(chalk.yellow("ℹ No history found. Push some files first."));
403
422
  console.log(chalk.blue.bold("\n📜 Push History:\n"));
404
423
  [...history].reverse().forEach((entry, i) => {
@@ -416,7 +435,7 @@ async function checkout(commitId) {
416
435
  checkDorkyProject();
417
436
  if (!await checkCredentials()) return;
418
437
 
419
- const history = existsSync(HISTORY_PATH) ? JSON.parse(readFileSync(HISTORY_PATH)) : [];
438
+ const history = readHistory();
420
439
  const entry = history.find(e => e.id === commitId || e.id.startsWith(commitId));
421
440
  if (!entry) return console.log(chalk.red(`✖ Commit not found: ${commitId}. Run --log to see available commits.`));
422
441
 
@@ -424,12 +443,12 @@ async function checkout(commitId) {
424
443
 
425
444
  const creds = readJson(CREDENTIALS_PATH);
426
445
  const root = path.basename(process.cwd());
427
- const historyPrefix = path.join(root, ".dorky-history", entry.id);
446
+ const historyPrefix = path.posix.join(root, ".dorky-history", entry.id);
428
447
 
429
448
  if (creds.storage === "aws") {
430
449
  await runS3(creds, async (s3, bucket) => {
431
450
  await Promise.all(Object.keys(entry.files).map(async f => {
432
- const key = path.join(historyPrefix, f);
451
+ const key = path.posix.join(historyPrefix, f);
433
452
  const { Body } = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
434
453
  if (!existsSync(path.dirname(f))) mkdirSync(path.dirname(f), { recursive: true });
435
454
  writeFileSync(f, await Body.transformToString());
@@ -439,10 +458,10 @@ async function checkout(commitId) {
439
458
  } else if (creds.storage === "google-drive") {
440
459
  await runDrive(async (drive) => {
441
460
  for (const f of Object.keys(entry.files)) {
442
- const parentId = await getFolderId(path.join(root, ".dorky-history", entry.id, path.dirname(f)), drive, false);
461
+ const parentId = await getFolderId(path.posix.join(root, ".dorky-history", entry.id, path.posix.dirname(f)), drive, false);
443
462
  if (!parentId) { console.log(chalk.red(`✖ Remote history folder missing for: ${f}`)); continue; }
444
463
  const res = await drive.files.list({
445
- q: `name='${path.basename(f)}' and '${parentId}' in parents and trashed=false`,
464
+ q: `name='${path.posix.basename(f)}' and '${parentId}' in parents and trashed=false`,
446
465
  fields: 'files(id)'
447
466
  });
448
467
  if (!res.data.files[0]) { console.log(chalk.red(`✖ Missing remote history file: ${f}`)); continue; }
@@ -454,7 +473,7 @@ async function checkout(commitId) {
454
473
  });
455
474
  }
456
475
 
457
- const meta = readJson(METADATA_PATH);
476
+ const meta = readMetadata();
458
477
  meta["stage-1-files"] = { ...entry.files };
459
478
  writeJson(METADATA_PATH, meta);
460
479
  console.log(chalk.cyan(`\nℹ Staged state restored to commit ${entry.id}. Run --push to publish this state.`));
package/bin/mcp.js CHANGED
@@ -24,6 +24,23 @@ const SCOPES = ["https://www.googleapis.com/auth/drive"];
24
24
  // Helpers
25
25
  const readJson = (p) => existsSync(p) ? JSON.parse(readFileSync(p)) : {};
26
26
  const writeJson = (p, d) => writeFileSync(p, JSON.stringify(d, null, 2));
27
+ const toPosix = (p) => p ? p.replace(/\\/g, '/') : p;
28
+ const normalizeKeys = (obj) => {
29
+ if (!obj) return {};
30
+ const out = {};
31
+ for (const k of Object.keys(obj)) out[toPosix(k)] = obj[k];
32
+ return out;
33
+ };
34
+ const readMetadata = () => {
35
+ const meta = readJson(METADATA_PATH);
36
+ meta["stage-1-files"] = normalizeKeys(meta["stage-1-files"]);
37
+ meta["uploaded-files"] = normalizeKeys(meta["uploaded-files"]);
38
+ return meta;
39
+ };
40
+ const readHistory = () => {
41
+ const history = existsSync(HISTORY_PATH) ? JSON.parse(readFileSync(HISTORY_PATH)) : [];
42
+ return history.map(e => ({ ...e, files: normalizeKeys(e.files) }));
43
+ };
27
44
 
28
45
  const checkDorkyProject = () => {
29
46
  if (!existsSync(DORKY_DIR) && !existsSync(".dorkyignore")) {
@@ -94,7 +111,7 @@ async function init(storage) {
94
111
 
95
112
  async function list(type) {
96
113
  checkDorkyProject();
97
- const meta = readJson(METADATA_PATH);
114
+ const meta = readMetadata();
98
115
  const lines = [];
99
116
 
100
117
  if (type === "remote") {
@@ -130,7 +147,7 @@ async function list(type) {
130
147
  const files = await glob("**/*", { dot: true, ignore: [...exclusions.map(e => `**/${e}/**`), ...exclusions, ".dorky/**", ".dorkyignore", ".git/**", "node_modules/**"] });
131
148
 
132
149
  files.forEach(f => {
133
- const rel = path.relative(process.cwd(), f);
150
+ const rel = toPosix(path.relative(process.cwd(), f));
134
151
  if (rel.includes(".env") || rel.includes(".config")) lines.push(` ${rel} (Potential sensitive file)`);
135
152
  else lines.push(` ${rel}`);
136
153
  });
@@ -143,14 +160,15 @@ async function list(type) {
143
160
 
144
161
  function add(files) {
145
162
  checkDorkyProject();
146
- const meta = readJson(METADATA_PATH);
163
+ const meta = readMetadata();
147
164
  const results = [];
148
165
  files.forEach(f => {
149
166
  if (!existsSync(f)) { results.push(`File not found: ${f}`); return; }
150
167
  const hash = md5(readFileSync(f));
151
- if (meta["stage-1-files"][f]?.hash === hash) { results.push(`${f} (unchanged)`); return; }
152
- meta["stage-1-files"][f] = { "mime-type": mimeTypes.lookup(f) || "application/octet-stream", hash };
153
- results.push(`Staged: ${f}`);
168
+ const key = toPosix(f);
169
+ if (meta["stage-1-files"][key]?.hash === hash) { results.push(`${key} (unchanged)`); return; }
170
+ meta["stage-1-files"][key] = { "mime-type": mimeTypes.lookup(f) || "application/octet-stream", hash };
171
+ results.push(`Staged: ${key}`);
154
172
  });
155
173
  writeJson(METADATA_PATH, meta);
156
174
  return results.join("\n");
@@ -158,10 +176,11 @@ function add(files) {
158
176
 
159
177
  function rm(files) {
160
178
  checkDorkyProject();
161
- const meta = readJson(METADATA_PATH);
179
+ const meta = readMetadata();
162
180
  const removed = files.filter(f => {
163
- if (!meta["stage-1-files"][f]) return false;
164
- delete meta["stage-1-files"][f];
181
+ const key = toPosix(f);
182
+ if (!meta["stage-1-files"][key]) return false;
183
+ delete meta["stage-1-files"][key];
165
184
  return true;
166
185
  });
167
186
  writeJson(METADATA_PATH, meta);
@@ -236,7 +255,7 @@ async function runDrive(fn) {
236
255
  async function push() {
237
256
  checkDorkyProject();
238
257
  if (!await checkCredentials()) return "Credentials not found. Please run init first.";
239
- const meta = readJson(METADATA_PATH);
258
+ const meta = readMetadata();
240
259
  const filesToUpload = Object.keys(meta["stage-1-files"])
241
260
  .filter(f => !meta["uploaded-files"][f] || meta["stage-1-files"][f].hash !== meta["uploaded-files"][f].hash)
242
261
  .map(f => ({ name: f, ...meta["stage-1-files"][f] }));
@@ -246,7 +265,7 @@ async function push() {
246
265
 
247
266
  const commitFiles = { ...meta["stage-1-files"] };
248
267
  const commitId = md5(JSON.stringify(commitFiles)).slice(0, 8);
249
- const history = existsSync(HISTORY_PATH) ? JSON.parse(readFileSync(HISTORY_PATH)) : [];
268
+ const history = readHistory();
250
269
  if (history.length > 0 && history[history.length - 1].id === commitId) return "Already on the latest commit. Nothing to push.";
251
270
 
252
271
  const creds = readJson(CREDENTIALS_PATH);
@@ -256,14 +275,14 @@ async function push() {
256
275
  await runS3(creds, async (s3, bucket) => {
257
276
  if (filesToUpload.length > 0) {
258
277
  await Promise.all(filesToUpload.map(async f => {
259
- const key = path.join(path.basename(process.cwd()), f.name);
278
+ const key = path.posix.join(path.basename(process.cwd()), f.name);
260
279
  await s3.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: readFileSync(f.name) }));
261
280
  results.push(`Uploaded: ${f.name}`);
262
281
  }));
263
282
  }
264
283
  if (filesToDelete.length > 0) {
265
284
  await Promise.all(filesToDelete.map(async f => {
266
- const key = path.join(path.basename(process.cwd()), f);
285
+ const key = path.posix.join(path.basename(process.cwd()), f);
267
286
  await s3.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
268
287
  results.push(`Deleted remote: ${f}`);
269
288
  }));
@@ -274,9 +293,9 @@ async function push() {
274
293
  if (filesToUpload.length > 0) {
275
294
  for (const f of filesToUpload) {
276
295
  const root = path.basename(process.cwd());
277
- const parentId = await getFolderId(path.dirname(path.join(root, f.name)), drive);
296
+ const parentId = await getFolderId(path.posix.dirname(path.posix.join(root, f.name)), drive);
278
297
  await drive.files.create({
279
- requestBody: { name: path.basename(f.name), parents: [parentId] },
298
+ requestBody: { name: path.posix.basename(f.name), parents: [parentId] },
280
299
  media: { mimeType: f["mime-type"], body: createReadStream(f.name) }
281
300
  });
282
301
  results.push(`Uploaded: ${f.name}`);
@@ -285,9 +304,9 @@ async function push() {
285
304
  if (filesToDelete.length > 0) {
286
305
  const root = path.basename(process.cwd());
287
306
  for (const f of filesToDelete) {
288
- const parentId = await getFolderId(path.dirname(path.join(root, f)), drive, false);
307
+ const parentId = await getFolderId(path.posix.dirname(path.posix.join(root, f)), drive, false);
289
308
  if (parentId) {
290
- const res = await drive.files.list({ q: `name='${path.basename(f)}' and '${parentId}' in parents and trashed=false`, fields: "files(id)" });
309
+ const res = await drive.files.list({ q: `name='${path.posix.basename(f)}' and '${parentId}' in parents and trashed=false`, fields: "files(id)" });
291
310
  if (res.data.files[0]) {
292
311
  await drive.files.delete({ fileId: res.data.files[0].id });
293
312
  results.push(`Deleted remote: ${f}`);
@@ -305,20 +324,20 @@ async function push() {
305
324
  writeJson(HISTORY_PATH, history);
306
325
 
307
326
  const root = path.basename(process.cwd());
308
- const historyPrefix = path.join(root, ".dorky-history", commitId);
327
+ const historyPrefix = path.posix.join(root, ".dorky-history", commitId);
309
328
  if (creds.storage === "aws") {
310
329
  await runS3(creds, async (s3, bucket) => {
311
330
  await Promise.all(Object.keys(commitFiles).map(async f => {
312
- const key = path.join(historyPrefix, f);
331
+ const key = path.posix.join(historyPrefix, f);
313
332
  await s3.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: readFileSync(f) }));
314
333
  }));
315
334
  });
316
335
  } else if (creds.storage === "google-drive") {
317
336
  await runDrive(async (drive) => {
318
337
  for (const f of Object.keys(commitFiles)) {
319
- const parentId = await getFolderId(path.join(root, ".dorky-history", commitId, path.dirname(f)), drive);
338
+ const parentId = await getFolderId(path.posix.join(root, ".dorky-history", commitId, path.posix.dirname(f)), drive);
320
339
  await drive.files.create({
321
- requestBody: { name: path.basename(f), parents: [parentId] },
340
+ requestBody: { name: path.posix.basename(f), parents: [parentId] },
322
341
  media: { mimeType: commitFiles[f]["mime-type"], body: createReadStream(f) }
323
342
  });
324
343
  }
@@ -332,7 +351,7 @@ async function push() {
332
351
  async function pull() {
333
352
  checkDorkyProject();
334
353
  if (!await checkCredentials()) return "Credentials not found. Please run init first.";
335
- const meta = readJson(METADATA_PATH);
354
+ const meta = readMetadata();
336
355
  const files = meta["uploaded-files"];
337
356
  const creds = readJson(CREDENTIALS_PATH);
338
357
  const results = [];
@@ -340,7 +359,7 @@ async function pull() {
340
359
  if (creds.storage === "aws") {
341
360
  await runS3(creds, async (s3, bucket) => {
342
361
  await Promise.all(Object.keys(files).map(async f => {
343
- const key = path.join(path.basename(process.cwd()), f);
362
+ const key = path.posix.join(path.basename(process.cwd()), f);
344
363
  const { Body } = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
345
364
  const dir = path.dirname(f);
346
365
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
@@ -352,7 +371,7 @@ async function pull() {
352
371
  await runDrive(async (drive) => {
353
372
  const fileList = Object.keys(files).map(k => ({ name: k, ...files[k] }));
354
373
  await Promise.all(fileList.map(async f => {
355
- const res = await drive.files.list({ q: `name='${path.basename(f.name)}' and mimeType!='application/vnd.google-apps.folder'`, fields: "files(id)" });
374
+ const res = await drive.files.list({ q: `name='${path.posix.basename(f.name)}' and mimeType!='application/vnd.google-apps.folder'`, fields: "files(id)" });
356
375
  if (!res.data.files[0]) { results.push(`Missing remote file: ${f.name}`); return; }
357
376
  const data = await drive.files.get({ fileId: res.data.files[0].id, alt: "media" });
358
377
  if (!existsSync(path.dirname(f.name))) mkdirSync(path.dirname(f.name), { recursive: true });
@@ -367,7 +386,7 @@ async function pull() {
367
386
 
368
387
  function log() {
369
388
  checkDorkyProject();
370
- const history = existsSync(HISTORY_PATH) ? JSON.parse(readFileSync(HISTORY_PATH)) : [];
389
+ const history = readHistory();
371
390
  if (!history.length) return "No history found. Push some files first.";
372
391
 
373
392
  const lines = ["Push History:"];
@@ -387,19 +406,19 @@ async function checkout(commitId) {
387
406
  checkDorkyProject();
388
407
  if (!await checkCredentials()) return "Credentials not found. Please run init first.";
389
408
 
390
- const history = existsSync(HISTORY_PATH) ? JSON.parse(readFileSync(HISTORY_PATH)) : [];
409
+ const history = readHistory();
391
410
  const entry = history.find(e => e.id === commitId || e.id.startsWith(commitId));
392
411
  if (!entry) return `Commit not found: ${commitId}. Run log to see available commits.`;
393
412
 
394
413
  const creds = readJson(CREDENTIALS_PATH);
395
414
  const root = path.basename(process.cwd());
396
- const historyPrefix = path.join(root, ".dorky-history", entry.id);
415
+ const historyPrefix = path.posix.join(root, ".dorky-history", entry.id);
397
416
  const results = [`Checking out commit ${entry.id} (${new Date(entry.timestamp).toLocaleString()}):`];
398
417
 
399
418
  if (creds.storage === "aws") {
400
419
  await runS3(creds, async (s3, bucket) => {
401
420
  await Promise.all(Object.keys(entry.files).map(async f => {
402
- const key = path.join(historyPrefix, f);
421
+ const key = path.posix.join(historyPrefix, f);
403
422
  const { Body } = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
404
423
  if (!existsSync(path.dirname(f))) mkdirSync(path.dirname(f), { recursive: true });
405
424
  writeFileSync(f, await Body.transformToString());
@@ -409,9 +428,9 @@ async function checkout(commitId) {
409
428
  } else if (creds.storage === "google-drive") {
410
429
  await runDrive(async (drive) => {
411
430
  for (const f of Object.keys(entry.files)) {
412
- const parentId = await getFolderId(path.join(root, ".dorky-history", entry.id, path.dirname(f)), drive, false);
431
+ const parentId = await getFolderId(path.posix.join(root, ".dorky-history", entry.id, path.posix.dirname(f)), drive, false);
413
432
  if (!parentId) { results.push(`Remote history folder missing for: ${f}`); continue; }
414
- const res = await drive.files.list({ q: `name='${path.basename(f)}' and '${parentId}' in parents and trashed=false`, fields: "files(id)" });
433
+ const res = await drive.files.list({ q: `name='${path.posix.basename(f)}' and '${parentId}' in parents and trashed=false`, fields: "files(id)" });
415
434
  if (!res.data.files[0]) { results.push(`Missing remote history file: ${f}`); continue; }
416
435
  const data = await drive.files.get({ fileId: res.data.files[0].id, alt: "media" });
417
436
  if (!existsSync(path.dirname(f))) mkdirSync(path.dirname(f), { recursive: true });
@@ -421,7 +440,7 @@ async function checkout(commitId) {
421
440
  });
422
441
  }
423
442
 
424
- const meta = readJson(METADATA_PATH);
443
+ const meta = readMetadata();
425
444
  meta["stage-1-files"] = { ...entry.files };
426
445
  writeJson(METADATA_PATH, meta);
427
446
  results.push(`Staged state restored to commit ${entry.id}. Run push to publish this state.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dorky",
3
- "version": "4.1.3",
3
+ "version": "4.1.4",
4
4
  "description": "DevOps Records Keeper.",
5
5
  "bin": {
6
6
  "dorky": "bin/index.js",