githits 0.1.0 → 0.1.2

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-2wbvwsc9.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 {
@@ -1597,7 +1709,6 @@ function createMcpServer(deps) {
1597
1709
  return server;
1598
1710
  }
1599
1711
  async function startMcpServer(deps) {
1600
- requireAuth(deps, "to start MCP server");
1601
1712
  const server = createMcpServer(deps);
1602
1713
  const transport = new StdioServerTransport;
1603
1714
  await server.connect(transport);
@@ -1629,27 +1740,15 @@ Available tools: search, search_language, feedback`).action(async () => {
1629
1740
  showMcpSetupInstructions();
1630
1741
  return;
1631
1742
  }
1632
- try {
1633
- const deps = await createContainer();
1634
- await startMcpServer(deps);
1635
- } catch (error) {
1636
- if (error instanceof AuthRequiredError)
1637
- process.exit(1);
1638
- throw error;
1639
- }
1743
+ const deps = await createContainer();
1744
+ await startMcpServer(deps);
1640
1745
  });
1641
1746
  mcpCommand.command("start").summary("Start MCP server (stdio mode)").description(`Start the MCP server using STDIO transport.
1642
1747
 
1643
1748
  This command explicitly starts the server and is intended for use
1644
1749
  in MCP configuration files. Use 'githits mcp' for interactive setup.`).action(async () => {
1645
- try {
1646
- const deps = await createContainer();
1647
- await startMcpServer(deps);
1648
- } catch (error) {
1649
- if (error instanceof AuthRequiredError)
1650
- process.exit(1);
1651
- throw error;
1652
- }
1750
+ const deps = await createContainer();
1751
+ await startMcpServer(deps);
1653
1752
  });
1654
1753
  }
1655
1754
  // src/commands/search.ts
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-2wbvwsc9.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.2";
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.2",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",