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-j0vey5g3.js";
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 keyring = new KeyringServiceImpl;
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
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  version
3
- } from "./shared/chunk-j0vey5g3.js";
3
+ } from "./shared/chunk-3ne7e7xh.js";
4
4
  export {
5
5
  version
6
6
  };
@@ -1,4 +1,4 @@
1
1
  // package.json
2
- var version = "0.1.0";
2
+ var version = "0.1.1";
3
3
 
4
4
  export { version };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "githits",
3
3
  "description": "CLI companion for GitHits - code examples from global open source for developers and AI assistants",
4
- "version": "0.1.0",
4
+ "version": "0.1.1",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",