thinkncollab-cli 0.0.30 → 0.0.32
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/index.js +45 -48
- package/commands/pull.js +16 -8
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -122,7 +122,7 @@ function scanFolder(folderPath, ignoreList, rootPath = folderPath) {
|
|
|
122
122
|
function loadVersions() {
|
|
123
123
|
if (!fs.existsSync(VERSION_FILE)) return {};
|
|
124
124
|
const data = JSON.parse(fs.readFileSync(VERSION_FILE, "utf-8"));
|
|
125
|
-
|
|
125
|
+
|
|
126
126
|
// Backward compatibility: if old format (just strings), convert to new format
|
|
127
127
|
const converted = {};
|
|
128
128
|
for (const [path, value] of Object.entries(data)) {
|
|
@@ -142,15 +142,15 @@ function saveVersions(versionMap) {
|
|
|
142
142
|
function computeFileHash(filePath) {
|
|
143
143
|
try {
|
|
144
144
|
if (!fs.existsSync(filePath)) return null;
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
const stats = fs.statSync(filePath);
|
|
147
147
|
if (stats.isDirectory()) {
|
|
148
148
|
// For folders: hash of sorted file names + their hashes
|
|
149
149
|
const items = fs.readdirSync(filePath).sort();
|
|
150
|
-
const childrenHashes = items.map(name =>
|
|
150
|
+
const childrenHashes = items.map(name =>
|
|
151
151
|
computeFileHash(path.join(filePath, name))
|
|
152
152
|
).filter(Boolean).join('|');
|
|
153
|
-
|
|
153
|
+
|
|
154
154
|
return crypto.createHash("sha256")
|
|
155
155
|
.update(`folder:${filePath}|${childrenHashes}`)
|
|
156
156
|
.digest("hex");
|
|
@@ -158,7 +158,7 @@ function computeFileHash(filePath) {
|
|
|
158
158
|
// For files: hash of content + file metadata
|
|
159
159
|
const content = fs.readFileSync(filePath);
|
|
160
160
|
const stats = fs.statSync(filePath);
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
return crypto.createHash("sha256")
|
|
163
163
|
.update(content)
|
|
164
164
|
.update(`size:${stats.size}|mtime:${stats.mtimeMs}`)
|
|
@@ -174,14 +174,14 @@ function checkChanges(fileTree, versionMap, rootPath = CWD) {
|
|
|
174
174
|
return fileTree.map(item => {
|
|
175
175
|
const fullPath = path.join(rootPath, item.path || item.name);
|
|
176
176
|
const relativePath = item.path || item.name;
|
|
177
|
-
|
|
177
|
+
|
|
178
178
|
let hash = null;
|
|
179
179
|
let changed = true;
|
|
180
180
|
|
|
181
181
|
try {
|
|
182
182
|
if (fs.existsSync(fullPath)) {
|
|
183
183
|
hash = computeFileHash(fullPath);
|
|
184
|
-
|
|
184
|
+
|
|
185
185
|
// Check against previous version
|
|
186
186
|
const prevVersion = versionMap[relativePath];
|
|
187
187
|
if (prevVersion && prevVersion.hash === hash) {
|
|
@@ -192,9 +192,9 @@ function checkChanges(fileTree, versionMap, rootPath = CWD) {
|
|
|
192
192
|
console.error(`❌ Error checking changes for ${relativePath}:`, err.message);
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
const newItem = {
|
|
196
|
-
...item,
|
|
197
|
-
changed,
|
|
195
|
+
const newItem = {
|
|
196
|
+
...item,
|
|
197
|
+
changed,
|
|
198
198
|
hash,
|
|
199
199
|
path: relativePath // Ensure consistent path
|
|
200
200
|
};
|
|
@@ -246,24 +246,24 @@ async function uploadTree(fileTree, folderHex, roomId, token, email, previousVer
|
|
|
246
246
|
|
|
247
247
|
if (node.type === "folder") {
|
|
248
248
|
const children = await uploadTree(node.children, folderHex, roomId, token, email, previousVersions, relativePath);
|
|
249
|
-
|
|
250
|
-
uploaded.push({
|
|
251
|
-
...node,
|
|
252
|
-
children,
|
|
249
|
+
|
|
250
|
+
uploaded.push({
|
|
251
|
+
...node,
|
|
252
|
+
children,
|
|
253
253
|
hash: node.hash,
|
|
254
|
-
path: relativePath
|
|
254
|
+
path: relativePath
|
|
255
255
|
});
|
|
256
256
|
} else {
|
|
257
257
|
const prevFile = previousVersions[relativePath];
|
|
258
|
-
|
|
258
|
+
|
|
259
259
|
if (node.changed) {
|
|
260
260
|
try {
|
|
261
261
|
console.log(`📤 Uploading changed file: ${relativePath}`);
|
|
262
262
|
const url = await uploadFileSigned(node.path, `tnc_uploads/${folderHex}`, roomId, token, email);
|
|
263
263
|
console.log(`✅ Uploaded: ${relativePath}`);
|
|
264
|
-
|
|
265
|
-
uploaded.push({
|
|
266
|
-
...node,
|
|
264
|
+
|
|
265
|
+
uploaded.push({
|
|
266
|
+
...node,
|
|
267
267
|
url,
|
|
268
268
|
hash: node.hash,
|
|
269
269
|
path: relativePath,
|
|
@@ -272,8 +272,8 @@ async function uploadTree(fileTree, folderHex, roomId, token, email, previousVer
|
|
|
272
272
|
} catch (err) {
|
|
273
273
|
console.error(`❌ Failed to upload ${relativePath}:`, err.message);
|
|
274
274
|
// Fallback to previous version if upload fails
|
|
275
|
-
uploaded.push({
|
|
276
|
-
...node,
|
|
275
|
+
uploaded.push({
|
|
276
|
+
...node,
|
|
277
277
|
url: prevFile?.url,
|
|
278
278
|
hash: node.hash,
|
|
279
279
|
path: relativePath,
|
|
@@ -283,8 +283,8 @@ async function uploadTree(fileTree, folderHex, roomId, token, email, previousVer
|
|
|
283
283
|
} else {
|
|
284
284
|
// Unchanged file - use previous URL and version
|
|
285
285
|
console.log(`📋 Using previous version for: ${relativePath}`);
|
|
286
|
-
uploaded.push({
|
|
287
|
-
...node,
|
|
286
|
+
uploaded.push({
|
|
287
|
+
...node,
|
|
288
288
|
url: prevFile?.url,
|
|
289
289
|
hash: node.hash,
|
|
290
290
|
path: relativePath,
|
|
@@ -329,11 +329,11 @@ async function push(roomId, targetPath) {
|
|
|
329
329
|
|
|
330
330
|
const previousVersions = loadVersions();
|
|
331
331
|
console.log('📁 Previous versions:', Object.keys(previousVersions).length);
|
|
332
|
-
|
|
332
|
+
|
|
333
333
|
const contentWithChanges = checkChanges(content, previousVersions);
|
|
334
|
-
|
|
334
|
+
|
|
335
335
|
// Debug: Show what changed
|
|
336
|
-
const changedFiles = contentWithChanges.flatMap(item =>
|
|
336
|
+
const changedFiles = contentWithChanges.flatMap(item =>
|
|
337
337
|
item.changed ? [item.path] : []
|
|
338
338
|
);
|
|
339
339
|
console.log('🔄 Changed files:', changedFiles.length, changedFiles);
|
|
@@ -417,29 +417,26 @@ async function main() {
|
|
|
417
417
|
break;
|
|
418
418
|
|
|
419
419
|
case "pull": {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const roomId = args[roomIndex + 1];
|
|
429
|
-
let version = "latest";
|
|
430
|
-
if (versionIndex !== -1 && args[versionIndex + 1]) {
|
|
431
|
-
version = args[versionIndex + 1];
|
|
432
|
-
} else {
|
|
433
|
-
const possibleVersionIndex = roomIndex + 2;
|
|
434
|
-
if (args[possibleVersionIndex] && !args[possibleVersionIndex].startsWith("--")) {
|
|
435
|
-
version = args[possibleVersionIndex];
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
await pull(roomId, version);
|
|
440
|
-
break;
|
|
441
|
-
}
|
|
420
|
+
const roomIndex = args.indexOf("--room");
|
|
421
|
+
|
|
422
|
+
if (roomIndex === -1 || !args[roomIndex + 1]) {
|
|
423
|
+
console.error("Usage: tnc pull --room <roomId> [--version <version>]");
|
|
424
|
+
process.exit(1);
|
|
425
|
+
}
|
|
442
426
|
|
|
427
|
+
const roomId = args[roomIndex + 1];
|
|
428
|
+
let version = "latest";
|
|
429
|
+
|
|
430
|
+
// Check for --version flag
|
|
431
|
+
const versionIndex = args.indexOf("--version");
|
|
432
|
+
if (versionIndex !== -1 && args[versionIndex + 1]) {
|
|
433
|
+
version = args[versionIndex + 1];
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
await pull(roomId, version);
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
|
|
443
440
|
default:
|
|
444
441
|
console.log("✅ TNC CLI ready!");
|
|
445
442
|
console.log("Commands:");
|
package/commands/pull.js
CHANGED
|
@@ -57,22 +57,30 @@ async function processFolderContent(content, basePath = "") {
|
|
|
57
57
|
const relPath = item.path ? item.path.replace(/^[\\/]+/, "") : item.name;
|
|
58
58
|
const itemPath = path.join(basePath, relPath);
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
// ✅ Skip .tnc folder and its contents
|
|
61
|
+
if (relPath.includes('.tnc') || itemPath.includes('.tnc')) {
|
|
62
|
+
console.log(chalk.gray(`⏭️ Skipping .tnc folder: ${itemPath}`));
|
|
63
|
+
skippedCount++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
65
66
|
|
|
67
|
+
// ✅ ONLY PROCESS FILES - Skip folders entirely
|
|
68
|
+
if (item.type === "folder") {
|
|
69
|
+
// Skip folder processing - we only care about files
|
|
66
70
|
if (item.children && item.children.length > 0) {
|
|
71
|
+
// Recursively process children files, but don't create folders
|
|
67
72
|
const result = await processFolderContent(item.children, itemPath);
|
|
68
73
|
downloadedCount += result.downloadedCount;
|
|
69
74
|
skippedCount += result.skippedCount;
|
|
70
75
|
errorCount += result.errorCount;
|
|
71
76
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
fs.mkdirSync(path.dirname(itemPath), { recursive: true });
|
|
77
|
+
continue; // Skip the folder itself
|
|
78
|
+
}
|
|
75
79
|
|
|
80
|
+
// ✅ ONLY FILE PROCESSING LOGIC
|
|
81
|
+
if (item.type === "file" && item.url) {
|
|
82
|
+
try {
|
|
83
|
+
// downloadFile will create necessary directories automatically
|
|
76
84
|
if (fs.existsSync(itemPath)) {
|
|
77
85
|
const existingStats = fs.statSync(itemPath);
|
|
78
86
|
if (existingStats.size === item.size) {
|