githits 0.1.0 → 0.1.1
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/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
version
|
|
4
|
-
} from "./shared/chunk-
|
|
4
|
+
} from "./shared/chunk-3ne7e7xh.js";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -424,6 +424,117 @@ class BrowserServiceImpl {
|
|
|
424
424
|
await open(url);
|
|
425
425
|
}
|
|
426
426
|
}
|
|
427
|
+
// src/services/chunking-keyring-service.ts
|
|
428
|
+
var WINDOWS_MAX_ENTRY_SIZE = 1200;
|
|
429
|
+
var CHUNKED_PREFIX = "CHUNKED:";
|
|
430
|
+
var MAX_CHUNK_COUNT = 100;
|
|
431
|
+
function chunkKey(account, writeId, index) {
|
|
432
|
+
return `${account}:chunk:${writeId}:${index}`;
|
|
433
|
+
}
|
|
434
|
+
function parseChunkedSentinel(value) {
|
|
435
|
+
if (!value.startsWith(CHUNKED_PREFIX))
|
|
436
|
+
return null;
|
|
437
|
+
const rest = value.slice(CHUNKED_PREFIX.length);
|
|
438
|
+
const colonIndex = rest.indexOf(":");
|
|
439
|
+
if (colonIndex === -1)
|
|
440
|
+
return null;
|
|
441
|
+
const writeId = rest.slice(0, colonIndex);
|
|
442
|
+
if (writeId.length === 0)
|
|
443
|
+
return null;
|
|
444
|
+
const countStr = rest.slice(colonIndex + 1);
|
|
445
|
+
const count = Number(countStr);
|
|
446
|
+
if (!Number.isInteger(count) || count <= 0)
|
|
447
|
+
return null;
|
|
448
|
+
return { writeId, count };
|
|
449
|
+
}
|
|
450
|
+
function splitIntoChunks(value, maxSize) {
|
|
451
|
+
if (value.length === 0)
|
|
452
|
+
return [""];
|
|
453
|
+
const chunks = [];
|
|
454
|
+
for (let offset = 0;offset < value.length; offset += maxSize) {
|
|
455
|
+
chunks.push(value.slice(offset, offset + maxSize));
|
|
456
|
+
}
|
|
457
|
+
return chunks;
|
|
458
|
+
}
|
|
459
|
+
function generateWriteId() {
|
|
460
|
+
let id;
|
|
461
|
+
do {
|
|
462
|
+
id = Math.random().toString(36).slice(2, 8);
|
|
463
|
+
} while (id.length < 6);
|
|
464
|
+
return id;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
class ChunkingKeyringService {
|
|
468
|
+
inner;
|
|
469
|
+
maxEntrySize;
|
|
470
|
+
constructor(inner, maxEntrySize = WINDOWS_MAX_ENTRY_SIZE) {
|
|
471
|
+
this.inner = inner;
|
|
472
|
+
this.maxEntrySize = maxEntrySize;
|
|
473
|
+
}
|
|
474
|
+
getPassword(service, account) {
|
|
475
|
+
const value = this.inner.getPassword(service, account);
|
|
476
|
+
if (value === null)
|
|
477
|
+
return null;
|
|
478
|
+
if (!value.startsWith(CHUNKED_PREFIX))
|
|
479
|
+
return value;
|
|
480
|
+
const sentinel = parseChunkedSentinel(value);
|
|
481
|
+
if (sentinel === null)
|
|
482
|
+
return null;
|
|
483
|
+
const chunks = [];
|
|
484
|
+
for (let i = 0;i < sentinel.count; i++) {
|
|
485
|
+
const chunk = this.inner.getPassword(service, chunkKey(account, sentinel.writeId, i));
|
|
486
|
+
if (chunk === null) {
|
|
487
|
+
console.error(`Warning: Incomplete chunked keychain entry for "${account}" (missing chunk ${i} of ${sentinel.count}). Treating as missing.`);
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
chunks.push(chunk);
|
|
491
|
+
}
|
|
492
|
+
return chunks.join("");
|
|
493
|
+
}
|
|
494
|
+
setPassword(service, account, password) {
|
|
495
|
+
const oldValue = this.readOldSentinel(service, account);
|
|
496
|
+
if (password.length <= this.maxEntrySize) {
|
|
497
|
+
this.inner.setPassword(service, account, password);
|
|
498
|
+
} else {
|
|
499
|
+
const chunks = splitIntoChunks(password, this.maxEntrySize);
|
|
500
|
+
if (chunks.length > MAX_CHUNK_COUNT) {
|
|
501
|
+
throw new Error(`Value requires ${chunks.length} chunks, exceeding maximum of ${MAX_CHUNK_COUNT}. ` + `This likely indicates a bug — credential data should not be this large.`);
|
|
502
|
+
}
|
|
503
|
+
const writeId = generateWriteId();
|
|
504
|
+
for (const [i, chunk] of chunks.entries()) {
|
|
505
|
+
this.inner.setPassword(service, chunkKey(account, writeId, i), chunk);
|
|
506
|
+
}
|
|
507
|
+
this.inner.setPassword(service, account, `${CHUNKED_PREFIX}${writeId}:${chunks.length}`);
|
|
508
|
+
}
|
|
509
|
+
if (oldValue !== null) {
|
|
510
|
+
this.deleteChunkEntries(service, account, oldValue);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
deletePassword(service, account) {
|
|
514
|
+
const oldValue = this.readOldSentinel(service, account);
|
|
515
|
+
if (oldValue !== null) {
|
|
516
|
+
this.deleteChunkEntries(service, account, oldValue);
|
|
517
|
+
}
|
|
518
|
+
return this.inner.deletePassword(service, account);
|
|
519
|
+
}
|
|
520
|
+
readOldSentinel(service, account) {
|
|
521
|
+
try {
|
|
522
|
+
const value = this.inner.getPassword(service, account);
|
|
523
|
+
if (value === null)
|
|
524
|
+
return null;
|
|
525
|
+
return parseChunkedSentinel(value);
|
|
526
|
+
} catch {
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
deleteChunkEntries(service, account, sentinel) {
|
|
531
|
+
for (let i = 0;i < sentinel.count; i++) {
|
|
532
|
+
try {
|
|
533
|
+
this.inner.deletePassword(service, chunkKey(account, sentinel.writeId, i));
|
|
534
|
+
} catch {}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
427
538
|
// src/services/config.ts
|
|
428
539
|
var DEFAULT_MCP_URL = "https://mcp.githits.com";
|
|
429
540
|
var DEFAULT_API_URL = "https://api.githits.com";
|
|
@@ -928,7 +1039,8 @@ class TokenManager {
|
|
|
928
1039
|
function createAuthStorage(fileSystemService) {
|
|
929
1040
|
const fileStorage = new AuthStorageImpl(fileSystemService);
|
|
930
1041
|
try {
|
|
931
|
-
const
|
|
1042
|
+
const rawKeyring = new KeyringServiceImpl;
|
|
1043
|
+
const keyring = process.platform === "win32" ? new ChunkingKeyringService(rawKeyring, WINDOWS_MAX_ENTRY_SIZE) : rawKeyring;
|
|
932
1044
|
const probeKey = `__probe_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
933
1045
|
keyring.setPassword("githits", probeKey, "probe");
|
|
934
1046
|
try {
|
package/dist/index.js
CHANGED