hanzi-browse 2.2.0

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 (78) hide show
  1. package/README.md +182 -0
  2. package/dist/agent/loop.d.ts +63 -0
  3. package/dist/agent/loop.js +186 -0
  4. package/dist/agent/system-prompt.d.ts +7 -0
  5. package/dist/agent/system-prompt.js +41 -0
  6. package/dist/agent/tools.d.ts +9 -0
  7. package/dist/agent/tools.js +154 -0
  8. package/dist/cli/detect-credentials.d.ts +31 -0
  9. package/dist/cli/detect-credentials.js +44 -0
  10. package/dist/cli/import-credentials-handler.d.ts +14 -0
  11. package/dist/cli/import-credentials-handler.js +22 -0
  12. package/dist/cli/session-files.d.ts +28 -0
  13. package/dist/cli/session-files.js +118 -0
  14. package/dist/cli/setup.d.ts +10 -0
  15. package/dist/cli/setup.js +915 -0
  16. package/dist/cli.d.ts +16 -0
  17. package/dist/cli.js +506 -0
  18. package/dist/dashboard/assets/index-CEFyesbT.js +46 -0
  19. package/dist/dashboard/assets/index-Dnht2kLU.css +1 -0
  20. package/dist/dashboard/index.html +13 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +1116 -0
  23. package/dist/ipc/index.d.ts +8 -0
  24. package/dist/ipc/index.js +8 -0
  25. package/dist/ipc/native-host.d.ts +96 -0
  26. package/dist/ipc/native-host.js +223 -0
  27. package/dist/ipc/websocket-client.d.ts +73 -0
  28. package/dist/ipc/websocket-client.js +199 -0
  29. package/dist/license/manager.d.ts +20 -0
  30. package/dist/license/manager.js +15 -0
  31. package/dist/llm/client.d.ts +72 -0
  32. package/dist/llm/client.js +227 -0
  33. package/dist/llm/credentials.d.ts +61 -0
  34. package/dist/llm/credentials.js +200 -0
  35. package/dist/llm/vertex.d.ts +22 -0
  36. package/dist/llm/vertex.js +335 -0
  37. package/dist/managed/api-http.test.d.ts +7 -0
  38. package/dist/managed/api-http.test.js +623 -0
  39. package/dist/managed/api.d.ts +51 -0
  40. package/dist/managed/api.js +1448 -0
  41. package/dist/managed/api.test.d.ts +10 -0
  42. package/dist/managed/api.test.js +146 -0
  43. package/dist/managed/auth.d.ts +38 -0
  44. package/dist/managed/auth.js +192 -0
  45. package/dist/managed/billing.d.ts +70 -0
  46. package/dist/managed/billing.js +227 -0
  47. package/dist/managed/deploy.d.ts +17 -0
  48. package/dist/managed/deploy.js +385 -0
  49. package/dist/managed/e2e.test.d.ts +15 -0
  50. package/dist/managed/e2e.test.js +151 -0
  51. package/dist/managed/hardening.test.d.ts +14 -0
  52. package/dist/managed/hardening.test.js +346 -0
  53. package/dist/managed/integration.test.d.ts +8 -0
  54. package/dist/managed/integration.test.js +274 -0
  55. package/dist/managed/log.d.ts +18 -0
  56. package/dist/managed/log.js +31 -0
  57. package/dist/managed/server.d.ts +12 -0
  58. package/dist/managed/server.js +69 -0
  59. package/dist/managed/store-pg.d.ts +191 -0
  60. package/dist/managed/store-pg.js +479 -0
  61. package/dist/managed/store.d.ts +188 -0
  62. package/dist/managed/store.js +379 -0
  63. package/dist/relay/auto-start.d.ts +19 -0
  64. package/dist/relay/auto-start.js +71 -0
  65. package/dist/relay/server.d.ts +17 -0
  66. package/dist/relay/server.js +403 -0
  67. package/dist/types/index.d.ts +5 -0
  68. package/dist/types/index.js +4 -0
  69. package/dist/types/session.d.ts +134 -0
  70. package/dist/types/session.js +16 -0
  71. package/package.json +61 -0
  72. package/skills/README.md +48 -0
  73. package/skills/a11y-auditor/SKILL.md +42 -0
  74. package/skills/e2e-tester/SKILL.md +154 -0
  75. package/skills/hanzi-browse/SKILL.md +182 -0
  76. package/skills/linkedin-prospector/SKILL.md +149 -0
  77. package/skills/social-poster/SKILL.md +146 -0
  78. package/skills/x-marketer/SKILL.md +479 -0
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Credential source detection for CLI setup.
3
+ *
4
+ * Claude Code stores OAuth tokens in one of two locations:
5
+ * 1. ~/.claude/.credentials.json (file-based, all platforms)
6
+ * 2. macOS Keychain under "Claude Code-credentials" (macOS only)
7
+ *
8
+ * The original implementation only checked (1), missing most macOS users.
9
+ */
10
+ import { join } from 'path';
11
+ // ── Constants ────────────────────────────────────────────────────────
12
+ const KEYCHAIN_SERVICE = 'Claude Code-credentials';
13
+ // ── Detection ────────────────────────────────────────────────────────
14
+ export function detectCredentialSources(opts) {
15
+ const { platform, homedir, fileExists, keychainHas } = opts;
16
+ const found = [];
17
+ const claudePath = join(homedir, '.claude', '.credentials.json');
18
+ if (fileExists(claudePath)) {
19
+ found.push({ name: 'Claude Code', slug: 'claude', path: claudePath });
20
+ }
21
+ else if (platform === 'darwin' && keychainHas(KEYCHAIN_SERVICE)) {
22
+ found.push({ name: 'Claude Code', slug: 'claude', path: 'macOS Keychain' });
23
+ }
24
+ const codexPath = join(homedir, '.codex', 'auth.json');
25
+ if (fileExists(codexPath)) {
26
+ found.push({ name: 'Codex CLI', slug: 'codex', path: codexPath });
27
+ }
28
+ return found;
29
+ }
30
+ // ── Flow state check ─────────────────────────────────────────────────
31
+ /**
32
+ * Returns an error message if setup finished with no credentials configured,
33
+ * or null if everything is fine.
34
+ */
35
+ export function checkCredentialFlowResult(state) {
36
+ if (state.sourcesDetected === 0)
37
+ return null;
38
+ if (state.anyImported)
39
+ return null;
40
+ if (state.manualEntryChosen)
41
+ return null;
42
+ return 'No credentials configured. The extension needs a model source to run tasks.\n'
43
+ + 'Add one later in the Chrome extension sidepanel → Settings.';
44
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Credential import handler for mcp-bridge.
3
+ *
4
+ * The original mcp-bridge used chrome.runtime.sendMessage() which cannot
5
+ * message the service worker from within itself ("Receiving end does not
6
+ * exist"). This provides a direct-call handler using Result types.
7
+ */
8
+ import { ResultAsync } from 'neverthrow';
9
+ export interface ImportDeps {
10
+ importCLI: () => Promise<unknown>;
11
+ importCodex: () => Promise<unknown>;
12
+ loadConfig: () => Promise<null>;
13
+ }
14
+ export declare function handleImportCredentials(source: unknown, deps: ImportDeps): ResultAsync<unknown, string>;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Credential import handler for mcp-bridge.
3
+ *
4
+ * The original mcp-bridge used chrome.runtime.sendMessage() which cannot
5
+ * message the service worker from within itself ("Receiving end does not
6
+ * exist"). This provides a direct-call handler using Result types.
7
+ */
8
+ import { err, ResultAsync } from 'neverthrow';
9
+ // ── Guard ────────────────────────────────────────────────────────────
10
+ function isValidSource(source) {
11
+ return source === 'claude' || source === 'codex';
12
+ }
13
+ // ── Handler ──────────────────────────────────────────────────────────
14
+ export function handleImportCredentials(source, deps) {
15
+ if (!isValidSource(source)) {
16
+ return new ResultAsync(Promise.resolve(err(`Unknown credential source: ${source}`)));
17
+ }
18
+ const importFn = source === 'claude' ? deps.importCLI : deps.importCodex;
19
+ return ResultAsync.fromPromise(importFn(), (e) => e.message)
20
+ .andThen((credentials) => ResultAsync.fromPromise(deps.loadConfig(), (e) => e.message)
21
+ .map(() => credentials));
22
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Session Files Module
3
+ *
4
+ * Manages file-based session storage for the CLI.
5
+ * Sessions are stored as JSON files in ~/.hanzi-browse/sessions/
6
+ */
7
+ export interface SessionFileStatus {
8
+ session_id: string;
9
+ status: 'starting' | 'running' | 'complete' | 'error' | 'stopped';
10
+ task: string;
11
+ url?: string;
12
+ context?: string;
13
+ started_at: string;
14
+ updated_at: string;
15
+ result?: string;
16
+ error?: string;
17
+ }
18
+ export declare function ensureSessionDir(): void;
19
+ export declare function getSessionFilePath(sessionId: string): string;
20
+ export declare function getSessionLogPath(sessionId: string): string;
21
+ export declare function getSessionScreenshotPath(sessionId: string): string;
22
+ export declare function writeSessionStatus(sessionId: string, status: Partial<SessionFileStatus>): void;
23
+ export declare function readSessionStatus(sessionId: string): SessionFileStatus | null;
24
+ export declare function appendSessionLog(sessionId: string, message: string): void;
25
+ export declare function readSessionLog(sessionId: string, lines?: number): string;
26
+ export declare function listSessions(): SessionFileStatus[];
27
+ export declare function listActiveSessions(): SessionFileStatus[];
28
+ export declare function deleteSessionFiles(sessionId: string): boolean;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Session Files Module
3
+ *
4
+ * Manages file-based session storage for the CLI.
5
+ * Sessions are stored as JSON files in ~/.hanzi-browse/sessions/
6
+ */
7
+ import { readFileSync, writeFileSync, mkdirSync, readdirSync, existsSync, appendFileSync, unlinkSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { homedir } from 'os';
10
+ // Session directory
11
+ const SESSION_DIR = join(homedir(), '.hanzi-browse', 'sessions');
12
+ export function ensureSessionDir() {
13
+ mkdirSync(SESSION_DIR, { recursive: true });
14
+ }
15
+ export function getSessionFilePath(sessionId) {
16
+ return join(SESSION_DIR, `${sessionId}.json`);
17
+ }
18
+ export function getSessionLogPath(sessionId) {
19
+ return join(SESSION_DIR, `${sessionId}.log`);
20
+ }
21
+ export function getSessionScreenshotPath(sessionId) {
22
+ return join(SESSION_DIR, `${sessionId}.png`);
23
+ }
24
+ export function writeSessionStatus(sessionId, status) {
25
+ ensureSessionDir();
26
+ const filePath = getSessionFilePath(sessionId);
27
+ let current;
28
+ if (existsSync(filePath)) {
29
+ try {
30
+ current = JSON.parse(readFileSync(filePath, 'utf-8'));
31
+ }
32
+ catch {
33
+ current = createInitialStatus(sessionId);
34
+ }
35
+ }
36
+ else {
37
+ current = createInitialStatus(sessionId);
38
+ }
39
+ const updated = {
40
+ ...current,
41
+ ...status,
42
+ updated_at: new Date().toISOString(),
43
+ };
44
+ writeFileSync(filePath, JSON.stringify(updated, null, 2));
45
+ }
46
+ function createInitialStatus(sessionId) {
47
+ const now = new Date().toISOString();
48
+ return {
49
+ session_id: sessionId,
50
+ status: 'starting',
51
+ task: '',
52
+ started_at: now,
53
+ updated_at: now,
54
+ };
55
+ }
56
+ export function readSessionStatus(sessionId) {
57
+ const filePath = getSessionFilePath(sessionId);
58
+ if (!existsSync(filePath)) {
59
+ return null;
60
+ }
61
+ try {
62
+ return JSON.parse(readFileSync(filePath, 'utf-8'));
63
+ }
64
+ catch (err) {
65
+ console.error(`[Session] Failed to parse ${sessionId}.json:`, err.message);
66
+ return null;
67
+ }
68
+ }
69
+ export function appendSessionLog(sessionId, message) {
70
+ ensureSessionDir();
71
+ const logPath = getSessionLogPath(sessionId);
72
+ const timestamp = new Date().toISOString();
73
+ appendFileSync(logPath, `[${timestamp}] ${message}\n`);
74
+ }
75
+ export function readSessionLog(sessionId, lines) {
76
+ const logPath = getSessionLogPath(sessionId);
77
+ if (!existsSync(logPath)) {
78
+ return '';
79
+ }
80
+ const content = readFileSync(logPath, 'utf-8');
81
+ if (lines) {
82
+ const allLines = content.split('\n');
83
+ return allLines.slice(-lines).join('\n');
84
+ }
85
+ return content;
86
+ }
87
+ export function listSessions() {
88
+ ensureSessionDir();
89
+ const files = readdirSync(SESSION_DIR).filter(f => f.endsWith('.json'));
90
+ const sessions = [];
91
+ for (const file of files) {
92
+ const sessionId = file.replace('.json', '');
93
+ const status = readSessionStatus(sessionId);
94
+ if (status) {
95
+ sessions.push(status);
96
+ }
97
+ }
98
+ sessions.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime());
99
+ return sessions;
100
+ }
101
+ export function listActiveSessions() {
102
+ return listSessions().filter(s => s.status === 'starting' ||
103
+ s.status === 'running');
104
+ }
105
+ export function deleteSessionFiles(sessionId) {
106
+ const statusPath = getSessionFilePath(sessionId);
107
+ const logPath = getSessionLogPath(sessionId);
108
+ let deleted = false;
109
+ if (existsSync(statusPath)) {
110
+ unlinkSync(statusPath);
111
+ deleted = true;
112
+ }
113
+ if (existsSync(logPath)) {
114
+ unlinkSync(logPath);
115
+ deleted = true;
116
+ }
117
+ return deleted;
118
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `hanzi-browser setup` — auto-detect AI agents and inject MCP config.
3
+ *
4
+ * Scans the machine for Claude Code, Cursor, Windsurf, and Claude Desktop,
5
+ * then merges the Hanzi MCP server entry into each agent's config file.
6
+ */
7
+ export declare function runSetup(options?: {
8
+ only?: string;
9
+ yes?: boolean;
10
+ }): Promise<void>;