@redonvn/cli 0.1.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 (141) hide show
  1. package/README.md +99 -0
  2. package/bin/redai.js +22 -0
  3. package/dist/auth/client-id.d.ts +8 -0
  4. package/dist/auth/client-id.js +41 -0
  5. package/dist/auth/client-id.js.map +1 -0
  6. package/dist/auth/store.d.ts +16 -0
  7. package/dist/auth/store.js +39 -0
  8. package/dist/auth/store.js.map +1 -0
  9. package/dist/cli/commands/login.d.ts +6 -0
  10. package/dist/cli/commands/login.js +82 -0
  11. package/dist/cli/commands/login.js.map +1 -0
  12. package/dist/cli/commands/logout.d.ts +1 -0
  13. package/dist/cli/commands/logout.js +18 -0
  14. package/dist/cli/commands/logout.js.map +1 -0
  15. package/dist/cli/commands/oauth.d.ts +11 -0
  16. package/dist/cli/commands/oauth.js +311 -0
  17. package/dist/cli/commands/oauth.js.map +1 -0
  18. package/dist/cli/commands/serve.d.ts +7 -0
  19. package/dist/cli/commands/serve.js +24 -0
  20. package/dist/cli/commands/serve.js.map +1 -0
  21. package/dist/cli/commands/start.d.ts +1 -0
  22. package/dist/cli/commands/start.js +26 -0
  23. package/dist/cli/commands/start.js.map +1 -0
  24. package/dist/cli/commands/status.d.ts +1 -0
  25. package/dist/cli/commands/status.js +51 -0
  26. package/dist/cli/commands/status.js.map +1 -0
  27. package/dist/cli/index.d.ts +2 -0
  28. package/dist/cli/index.js +127 -0
  29. package/dist/cli/index.js.map +1 -0
  30. package/dist/cli-router/detect.d.ts +13 -0
  31. package/dist/cli-router/detect.js +58 -0
  32. package/dist/cli-router/detect.js.map +1 -0
  33. package/dist/config.d.ts +12 -0
  34. package/dist/config.js +24 -0
  35. package/dist/config.js.map +1 -0
  36. package/dist/daemon/router.d.ts +17 -0
  37. package/dist/daemon/router.js +39 -0
  38. package/dist/daemon/router.js.map +1 -0
  39. package/dist/daemon/tunnel.d.ts +10 -0
  40. package/dist/daemon/tunnel.js +156 -0
  41. package/dist/daemon/tunnel.js.map +1 -0
  42. package/dist/llm/llm-request.d.ts +9 -0
  43. package/dist/llm/llm-request.js +98 -0
  44. package/dist/llm/llm-request.js.map +1 -0
  45. package/dist/llm/oauth/antigravity-oauth.d.ts +22 -0
  46. package/dist/llm/oauth/antigravity-oauth.js +128 -0
  47. package/dist/llm/oauth/antigravity-oauth.js.map +1 -0
  48. package/dist/llm/oauth/callback-server.d.ts +18 -0
  49. package/dist/llm/oauth/callback-server.js +78 -0
  50. package/dist/llm/oauth/callback-server.js.map +1 -0
  51. package/dist/llm/oauth/claude-oauth.d.ts +29 -0
  52. package/dist/llm/oauth/claude-oauth.js +115 -0
  53. package/dist/llm/oauth/claude-oauth.js.map +1 -0
  54. package/dist/llm/oauth/codex-oauth.d.ts +21 -0
  55. package/dist/llm/oauth/codex-oauth.js +137 -0
  56. package/dist/llm/oauth/codex-oauth.js.map +1 -0
  57. package/dist/llm/oauth/gemini-oauth.d.ts +26 -0
  58. package/dist/llm/oauth/gemini-oauth.js +132 -0
  59. package/dist/llm/oauth/gemini-oauth.js.map +1 -0
  60. package/dist/llm/oauth/iflow-oauth.d.ts +26 -0
  61. package/dist/llm/oauth/iflow-oauth.js +151 -0
  62. package/dist/llm/oauth/iflow-oauth.js.map +1 -0
  63. package/dist/llm/oauth/kimi-oauth.d.ts +16 -0
  64. package/dist/llm/oauth/kimi-oauth.js +126 -0
  65. package/dist/llm/oauth/kimi-oauth.js.map +1 -0
  66. package/dist/llm/oauth/open-browser.d.ts +5 -0
  67. package/dist/llm/oauth/open-browser.js +32 -0
  68. package/dist/llm/oauth/open-browser.js.map +1 -0
  69. package/dist/llm/oauth/pkce.d.ts +12 -0
  70. package/dist/llm/oauth/pkce.js +27 -0
  71. package/dist/llm/oauth/pkce.js.map +1 -0
  72. package/dist/llm/oauth/qwen-oauth.d.ts +27 -0
  73. package/dist/llm/oauth/qwen-oauth.js +138 -0
  74. package/dist/llm/oauth/qwen-oauth.js.map +1 -0
  75. package/dist/llm/oauth/store.d.ts +34 -0
  76. package/dist/llm/oauth/store.js +72 -0
  77. package/dist/llm/oauth/store.js.map +1 -0
  78. package/dist/llm/oauth/xai-oauth.d.ts +28 -0
  79. package/dist/llm/oauth/xai-oauth.js +132 -0
  80. package/dist/llm/oauth/xai-oauth.js.map +1 -0
  81. package/dist/llm/providers/antigravity.d.ts +23 -0
  82. package/dist/llm/providers/antigravity.js +103 -0
  83. package/dist/llm/providers/antigravity.js.map +1 -0
  84. package/dist/llm/providers/claude.d.ts +36 -0
  85. package/dist/llm/providers/claude.js +148 -0
  86. package/dist/llm/providers/claude.js.map +1 -0
  87. package/dist/llm/providers/codex.d.ts +23 -0
  88. package/dist/llm/providers/codex.js +122 -0
  89. package/dist/llm/providers/codex.js.map +1 -0
  90. package/dist/llm/providers/gemini.d.ts +23 -0
  91. package/dist/llm/providers/gemini.js +112 -0
  92. package/dist/llm/providers/gemini.js.map +1 -0
  93. package/dist/llm/providers/generic-client.d.ts +45 -0
  94. package/dist/llm/providers/generic-client.js +98 -0
  95. package/dist/llm/providers/generic-client.js.map +1 -0
  96. package/dist/llm/providers/iflow.d.ts +8 -0
  97. package/dist/llm/providers/iflow.js +15 -0
  98. package/dist/llm/providers/iflow.js.map +1 -0
  99. package/dist/llm/providers/kimi.d.ts +8 -0
  100. package/dist/llm/providers/kimi.js +16 -0
  101. package/dist/llm/providers/kimi.js.map +1 -0
  102. package/dist/llm/providers/qwen.d.ts +8 -0
  103. package/dist/llm/providers/qwen.js +15 -0
  104. package/dist/llm/providers/qwen.js.map +1 -0
  105. package/dist/llm/providers/xai.d.ts +8 -0
  106. package/dist/llm/providers/xai.js +15 -0
  107. package/dist/llm/providers/xai.js.map +1 -0
  108. package/dist/llm/selector.d.ts +14 -0
  109. package/dist/llm/selector.js +81 -0
  110. package/dist/llm/selector.js.map +1 -0
  111. package/dist/server/http-server.d.ts +22 -0
  112. package/dist/server/http-server.js +194 -0
  113. package/dist/server/http-server.js.map +1 -0
  114. package/dist/tools/bash.d.ts +7 -0
  115. package/dist/tools/bash.js +61 -0
  116. package/dist/tools/bash.js.map +1 -0
  117. package/dist/tools/edit.d.ts +4 -0
  118. package/dist/tools/edit.js +35 -0
  119. package/dist/tools/edit.js.map +1 -0
  120. package/dist/tools/glob.d.ts +5 -0
  121. package/dist/tools/glob.js +27 -0
  122. package/dist/tools/glob.js.map +1 -0
  123. package/dist/tools/grep.d.ts +5 -0
  124. package/dist/tools/grep.js +63 -0
  125. package/dist/tools/grep.js.map +1 -0
  126. package/dist/tools/read.d.ts +6 -0
  127. package/dist/tools/read.js +25 -0
  128. package/dist/tools/read.js.map +1 -0
  129. package/dist/tools/registry.d.ts +8 -0
  130. package/dist/tools/registry.js +43 -0
  131. package/dist/tools/registry.js.map +1 -0
  132. package/dist/tools/run-cli.d.ts +7 -0
  133. package/dist/tools/run-cli.js +83 -0
  134. package/dist/tools/run-cli.js.map +1 -0
  135. package/dist/tools/webfetch.d.ts +7 -0
  136. package/dist/tools/webfetch.js +26 -0
  137. package/dist/tools/webfetch.js.map +1 -0
  138. package/dist/tools/write.d.ts +5 -0
  139. package/dist/tools/write.js +29 -0
  140. package/dist/tools/write.js.map +1 -0
  141. package/package.json +67 -0
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectCapabilities = detectCapabilities;
4
+ const child_process_1 = require("child_process");
5
+ const store_1 = require("../llm/oauth/store");
6
+ /**
7
+ * Detect AI-Agent CLI có sẵn trên máy. Trả:
8
+ * - capabilities: tổng hợp gồm 3 nhóm:
9
+ * 1. Tool runtime cơ bản: fs / bash / grep / glob / webfetch (luôn có)
10
+ * 2. Spawn CLI: claude / codex / gemini (có nếu binary cài sẵn)
11
+ * 3. LLM proxy: llm-claude / llm-codex / llm-gemini (có nếu user `redai oauth <provider>`)
12
+ * - cliDetected: { claude:'1.0.5', ... } cho dashboard hiển thị
13
+ */
14
+ function detectCapabilities() {
15
+ const cliDetected = {};
16
+ const capabilities = ['fs', 'bash', 'grep', 'glob', 'webfetch'];
17
+ // (2) Spawn-CLI capabilities — phụ thuộc binary cài sẵn
18
+ for (const cli of ['claude', 'codex', 'gemini']) {
19
+ const version = sniffVersion(cli);
20
+ if (version) {
21
+ cliDetected[cli] = version;
22
+ capabilities.push(cli);
23
+ }
24
+ }
25
+ // (3) LLM-proxy capabilities — phụ thuộc OAuth token đã lưu
26
+ for (const provider of [
27
+ 'claude',
28
+ 'codex',
29
+ 'gemini',
30
+ 'antigravity',
31
+ 'qwen',
32
+ 'kimi',
33
+ 'iflow',
34
+ 'xai',
35
+ ]) {
36
+ if ((0, store_1.listAccounts)(provider).length > 0) {
37
+ capabilities.push(`llm-${provider}`);
38
+ }
39
+ }
40
+ return { capabilities, cliDetected };
41
+ }
42
+ function sniffVersion(binary) {
43
+ for (const arg of ['--version', '-v', 'version']) {
44
+ const result = (0, child_process_1.spawnSync)(binary, [arg], {
45
+ encoding: 'utf8',
46
+ timeout: 3000,
47
+ windowsHide: true,
48
+ });
49
+ if (result.status === 0 && result.stdout) {
50
+ const match = result.stdout.match(/\d+(?:\.\d+){1,3}/);
51
+ if (match)
52
+ return match[0];
53
+ return result.stdout.trim().split('\n')[0]?.slice(0, 40) || null;
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/cli-router/detect.ts"],"names":[],"mappings":";;AAYA,gDAiCC;AA7CD,iDAA0C;AAE1C,8CAAkD;AAElD;;;;;;;GAOG;AACH,SAAgB,kBAAkB;IAIhC,MAAM,WAAW,GAA2B,EAAE,CAAC;IAC/C,MAAM,YAAY,GAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAE9E,wDAAwD;IACxD,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAU,EAAE,CAAC;QACzD,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,OAAO,EAAE,CAAC;YACZ,WAAW,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;YAC3B,YAAY,CAAC,IAAI,CAAC,GAAiB,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,KAAK,MAAM,QAAQ,IAAI;QACrB,QAAQ;QACR,OAAO;QACP,QAAQ;QACR,aAAa;QACb,MAAM;QACN,MAAM;QACN,OAAO;QACP,KAAK;KACG,EAAE,CAAC;QACX,IAAI,IAAA,oBAAY,EAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtC,YAAY,CAAC,IAAI,CAAC,OAAO,QAAQ,EAAgB,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AACvC,CAAC;AAED,SAAS,YAAY,CAAC,MAAc;IAClC,KAAK,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;QACjD,MAAM,MAAM,GAAG,IAAA,yBAAS,EAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE;YACtC,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,IAAK;YACd,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;YACvD,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3B,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC;QACnE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,12 @@
1
+ export declare const CLI_PACKAGE_NAME = "@redonvn/cli";
2
+ export declare const CLI_VERSION = "0.1.0";
3
+ export declare const REDAI_CONFIG_DIR: string;
4
+ export declare const REDAI_CONFIG_FILE: string;
5
+ export declare const REDAI_CLIENT_ID_FILE: string;
6
+ export declare const REDAI_LOG_FILE: string;
7
+ export declare const DEFAULT_API_BASE_URL: string;
8
+ /** WS URL — backend sẽ trả về wsUrl trong response /cli/auth/verify, đây chỉ là fallback. */
9
+ export declare const DEFAULT_WS_URL: string;
10
+ export declare const HEARTBEAT_INTERVAL_MS = 25000;
11
+ export declare const RECONNECT_BASE_DELAY_MS = 2000;
12
+ export declare const RECONNECT_MAX_DELAY_MS = 30000;
package/dist/config.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RECONNECT_MAX_DELAY_MS = exports.RECONNECT_BASE_DELAY_MS = exports.HEARTBEAT_INTERVAL_MS = exports.DEFAULT_WS_URL = exports.DEFAULT_API_BASE_URL = exports.REDAI_LOG_FILE = exports.REDAI_CLIENT_ID_FILE = exports.REDAI_CONFIG_FILE = exports.REDAI_CONFIG_DIR = exports.CLI_VERSION = exports.CLI_PACKAGE_NAME = void 0;
7
+ const os_1 = __importDefault(require("os"));
8
+ const path_1 = __importDefault(require("path"));
9
+ exports.CLI_PACKAGE_NAME = '@redonvn/cli';
10
+ exports.CLI_VERSION = '0.1.0';
11
+ exports.REDAI_CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.redai');
12
+ exports.REDAI_CONFIG_FILE = path_1.default.join(exports.REDAI_CONFIG_DIR, 'config.json');
13
+ exports.REDAI_CLIENT_ID_FILE = path_1.default.join(exports.REDAI_CONFIG_DIR, 'client.id');
14
+ exports.REDAI_LOG_FILE = path_1.default.join(exports.REDAI_CONFIG_DIR, 'daemon.log');
15
+ exports.DEFAULT_API_BASE_URL = process.env.REDAI_API_URL || 'https://redai-erp.io.vn';
16
+ /** WS URL — backend sẽ trả về wsUrl trong response /cli/auth/verify, đây chỉ là fallback. */
17
+ exports.DEFAULT_WS_URL = process.env.REDAI_WS_URL ||
18
+ (exports.DEFAULT_API_BASE_URL.startsWith('https')
19
+ ? exports.DEFAULT_API_BASE_URL.replace('https', 'wss')
20
+ : exports.DEFAULT_API_BASE_URL.replace('http', 'ws')) + '/redai-cli';
21
+ exports.HEARTBEAT_INTERVAL_MS = 25000;
22
+ exports.RECONNECT_BASE_DELAY_MS = 2000;
23
+ exports.RECONNECT_MAX_DELAY_MS = 30000;
24
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AAEX,QAAA,gBAAgB,GAAG,cAAc,CAAC;AAClC,QAAA,WAAW,GAAG,OAAO,CAAC;AAEtB,QAAA,gBAAgB,GAAG,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AACrD,QAAA,iBAAiB,GAAG,cAAI,CAAC,IAAI,CAAC,wBAAgB,EAAE,aAAa,CAAC,CAAC;AAC/D,QAAA,oBAAoB,GAAG,cAAI,CAAC,IAAI,CAAC,wBAAgB,EAAE,WAAW,CAAC,CAAC;AAChE,QAAA,cAAc,GAAG,cAAI,CAAC,IAAI,CAAC,wBAAgB,EAAE,YAAY,CAAC,CAAC;AAE3D,QAAA,oBAAoB,GAC/B,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,yBAAyB,CAAC;AAEzD,6FAA6F;AAChF,QAAA,cAAc,GACzB,OAAO,CAAC,GAAG,CAAC,YAAY;IACxB,CAAC,4BAAoB,CAAC,UAAU,CAAC,OAAO,CAAC;QACvC,CAAC,CAAC,4BAAoB,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC;QAC9C,CAAC,CAAC,4BAAoB,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,GAAG,YAAY,CAAC;AAEpD,QAAA,qBAAqB,GAAG,KAAM,CAAC;AAC/B,QAAA,uBAAuB,GAAG,IAAK,CAAC;AAChC,QAAA,sBAAsB,GAAG,KAAM,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { type Envelope } from '@redonvn/cli-protocol';
2
+ /**
3
+ * Route inbound TOOL_CALL envelope → đúng tool implementation.
4
+ *
5
+ * Trả về tuple { ok, result } | { ok:false, error } để daemon emit lại
6
+ * TOOL_RESULT về backend.
7
+ *
8
+ * `onStream` (optional): daemon truyền callback để LLMRequest (stream=true)
9
+ * forward SSE chunks qua TOOL_STREAM event ngược lên BE.
10
+ */
11
+ export declare function handleToolCall(env: Envelope<unknown>, opts?: {
12
+ onStream?: (chunk: string) => void;
13
+ }): Promise<{
14
+ ok: boolean;
15
+ result?: unknown;
16
+ error?: string;
17
+ }>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleToolCall = handleToolCall;
4
+ const cli_protocol_1 = require("@redonvn/cli-protocol");
5
+ const registry_1 = require("../tools/registry");
6
+ /**
7
+ * Route inbound TOOL_CALL envelope → đúng tool implementation.
8
+ *
9
+ * Trả về tuple { ok, result } | { ok:false, error } để daemon emit lại
10
+ * TOOL_RESULT về backend.
11
+ *
12
+ * `onStream` (optional): daemon truyền callback để LLMRequest (stream=true)
13
+ * forward SSE chunks qua TOOL_STREAM event ngược lên BE.
14
+ */
15
+ async function handleToolCall(env, opts = {}) {
16
+ if (env.type !== cli_protocol_1.CLI_ENVELOPE_TYPES.TOOL_CALL) {
17
+ return { ok: false, error: `not a TOOL_CALL envelope: ${env.type}` };
18
+ }
19
+ const parsed = cli_protocol_1.ToolCallPayloadSchema.safeParse(env.payload);
20
+ if (!parsed.success) {
21
+ return { ok: false, error: `invalid TOOL_CALL payload: ${parsed.error.message}` };
22
+ }
23
+ const payload = parsed.data;
24
+ try {
25
+ const result = await (0, registry_1.runTool)(payload.tool, payload.params, {
26
+ timeoutMs: payload.timeoutMs,
27
+ cwd: payload.cwd,
28
+ onStream: opts.onStream,
29
+ });
30
+ return { ok: true, result };
31
+ }
32
+ catch (err) {
33
+ return {
34
+ ok: false,
35
+ error: err instanceof Error ? err.message : String(err),
36
+ };
37
+ }
38
+ }
39
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/daemon/router.ts"],"names":[],"mappings":";;AAiBA,wCA+BC;AAhDD,wDAK+B;AAC/B,gDAAsD;AAEtD;;;;;;;;GAQG;AACI,KAAK,UAAU,cAAc,CAClC,GAAsB,EACtB,OAA+C,EAAE;IAMjD,IAAI,GAAG,CAAC,IAAI,KAAK,iCAAkB,CAAC,SAAS,EAAE,CAAC;QAC9C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,6BAA6B,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;IACvE,CAAC;IAED,MAAM,MAAM,GAAG,oCAAqB,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,8BAA8B,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;IACpF,CAAC;IAED,MAAM,OAAO,GAAoB,MAAM,CAAC,IAAI,CAAC;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAA,kBAAO,EAAC,OAAO,CAAC,IAAgB,EAAE,OAAO,CAAC,MAAM,EAAE;YACrE,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,QAAQ,EAAE,IAAI,CAAC,QAAQ;SACxB,CAAC,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { RedaiCliConfig } from '../auth/store';
2
+ /**
3
+ * Kết nối WS với backend và giữ sống cho đến khi user Ctrl-C.
4
+ *
5
+ * Pattern học từ desktop/main.js của proxy-desktop-app:
6
+ * - Outbound WSS (firewall-friendly, không cần mở port)
7
+ * - Auto-reconnect với exponential backoff (handled by socket.io-client)
8
+ * - HELLO + heartbeat
9
+ */
10
+ export declare function startDaemon(cfg: RedaiCliConfig): Promise<void>;
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.startDaemon = startDaemon;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const socket_io_client_1 = require("socket.io-client");
10
+ const cli_protocol_1 = require("@redonvn/cli-protocol");
11
+ const config_1 = require("../config");
12
+ const detect_1 = require("../cli-router/detect");
13
+ const router_1 = require("./router");
14
+ let activeSocket = null;
15
+ let heartbeatTimer = null;
16
+ let startedAt = 0;
17
+ /**
18
+ * Kết nối WS với backend và giữ sống cho đến khi user Ctrl-C.
19
+ *
20
+ * Pattern học từ desktop/main.js của proxy-desktop-app:
21
+ * - Outbound WSS (firewall-friendly, không cần mở port)
22
+ * - Auto-reconnect với exponential backoff (handled by socket.io-client)
23
+ * - HELLO + heartbeat
24
+ */
25
+ async function startDaemon(cfg) {
26
+ const wsUrl = cfg.wsUrl;
27
+ const { capabilities, cliDetected } = (0, detect_1.detectCapabilities)();
28
+ console.log(chalk_1.default.dim(`[ws] connecting to ${wsUrl}`));
29
+ // socket.io-client gắn namespace ngay trong URL nếu URL có path. Vì wsUrl
30
+ // server trả về dạng `wss://.../redai-cli` → tách host + namespace.
31
+ const url = new URL(wsUrl);
32
+ const namespace = url.pathname || '/';
33
+ const origin = `${url.protocol}//${url.host}`;
34
+ const socket = (0, socket_io_client_1.io)(`${origin}${namespace}`, {
35
+ transports: ['websocket'],
36
+ auth: { token: cfg.cliToken },
37
+ reconnection: true,
38
+ reconnectionDelay: 2000,
39
+ reconnectionDelayMax: 30000,
40
+ timeout: 15000,
41
+ });
42
+ activeSocket = socket;
43
+ startedAt = Date.now();
44
+ socket.on('connect', () => {
45
+ console.log(chalk_1.default.green('[ws] connected'), chalk_1.default.dim(`socket=${socket.id}`));
46
+ sendHello(socket, cfg, capabilities, cliDetected);
47
+ startHeartbeat(socket);
48
+ });
49
+ socket.on('disconnect', (reason) => {
50
+ console.log(chalk_1.default.yellow('[ws] disconnected'), chalk_1.default.dim(reason));
51
+ stopHeartbeat();
52
+ });
53
+ socket.on('connect_error', (err) => {
54
+ console.log(chalk_1.default.red('[ws] connect_error'), chalk_1.default.dim(err.message));
55
+ });
56
+ socket.on(cli_protocol_1.CLI_SOCKET_EVENTS.ERROR, (data) => {
57
+ console.log(chalk_1.default.red('[ws] server error'), data);
58
+ });
59
+ socket.on(cli_protocol_1.CLI_SOCKET_EVENTS.REVOKED, (data) => {
60
+ console.log(chalk_1.default.red('[ws] revoked by server'), data);
61
+ void shutdown(1);
62
+ });
63
+ socket.on(cli_protocol_1.CLI_SOCKET_EVENTS.ENVELOPE, async (raw) => {
64
+ try {
65
+ const env = raw;
66
+ await onEnvelope(socket, env);
67
+ }
68
+ catch (err) {
69
+ console.log(chalk_1.default.red('[ws] envelope handler error'), err instanceof Error ? err.message : err);
70
+ }
71
+ });
72
+ installSignalHandlers();
73
+ // Block forever — đợi shutdown
74
+ await new Promise(() => undefined);
75
+ }
76
+ function sendHello(socket, cfg, capabilities, cliDetected) {
77
+ socket.emit(cli_protocol_1.CLI_SOCKET_EVENTS.ENVELOPE, (0, cli_protocol_1.buildEnvelope)(cli_protocol_1.CLI_ENVELOPE_TYPES.HELLO, {
78
+ clientId: cfg.clientId,
79
+ hostname: os_1.default.hostname(),
80
+ platform: process.platform,
81
+ cliVersion: config_1.CLI_VERSION,
82
+ capabilities,
83
+ cliDetected,
84
+ }));
85
+ }
86
+ function startHeartbeat(socket) {
87
+ stopHeartbeat();
88
+ heartbeatTimer = setInterval(() => {
89
+ if (!socket.connected)
90
+ return;
91
+ socket.emit(cli_protocol_1.CLI_SOCKET_EVENTS.ENVELOPE, (0, cli_protocol_1.buildEnvelope)(cli_protocol_1.CLI_ENVELOPE_TYPES.HEARTBEAT, {
92
+ uptimeSec: Math.floor((Date.now() - startedAt) / 1000),
93
+ pendingRequests: 0,
94
+ }));
95
+ }, config_1.HEARTBEAT_INTERVAL_MS);
96
+ // Don't keep event loop alive solely for heartbeat — let user Ctrl-C exit
97
+ heartbeatTimer.unref?.();
98
+ }
99
+ function stopHeartbeat() {
100
+ if (heartbeatTimer) {
101
+ clearInterval(heartbeatTimer);
102
+ heartbeatTimer = null;
103
+ }
104
+ }
105
+ async function onEnvelope(socket, env) {
106
+ switch (env.type) {
107
+ case cli_protocol_1.CLI_ENVELOPE_TYPES.HELLO_ACK:
108
+ console.log(chalk_1.default.green('[ws] HELLO_ACK'), chalk_1.default.dim(JSON.stringify(env.payload)));
109
+ return;
110
+ case cli_protocol_1.CLI_ENVELOPE_TYPES.TOOL_CALL: {
111
+ const startedAtMs = Date.now();
112
+ const onStream = (chunk) => {
113
+ if (!socket.connected)
114
+ return;
115
+ socket.emit(cli_protocol_1.CLI_SOCKET_EVENTS.ENVELOPE, (0, cli_protocol_1.buildEnvelope)(cli_protocol_1.CLI_ENVELOPE_TYPES.TOOL_STREAM, { requestId: env.requestId ?? '', stream: 'stdout', data: chunk }, { requestId: env.requestId, sessionId: env.sessionId }));
116
+ };
117
+ const result = await (0, router_1.handleToolCall)(env, { onStream });
118
+ socket.emit(cli_protocol_1.CLI_SOCKET_EVENTS.ENVELOPE, (0, cli_protocol_1.buildEnvelope)(cli_protocol_1.CLI_ENVELOPE_TYPES.TOOL_RESULT, {
119
+ requestId: env.requestId ?? '',
120
+ ok: result.ok,
121
+ result: result.result,
122
+ error: result.error,
123
+ durationMs: Date.now() - startedAtMs,
124
+ }, { requestId: env.requestId, sessionId: env.sessionId }));
125
+ return;
126
+ }
127
+ case cli_protocol_1.CLI_ENVELOPE_TYPES.TOOL_CANCEL:
128
+ // TODO: implement cancellation tokens for long-running Bash/RunCli
129
+ return;
130
+ default:
131
+ // Unhandled types are ignored — server side has its own logging.
132
+ return;
133
+ }
134
+ }
135
+ function installSignalHandlers() {
136
+ for (const sig of ['SIGINT', 'SIGTERM']) {
137
+ process.on(sig, () => {
138
+ console.log(chalk_1.default.dim(`\n[daemon] received ${sig}, shutting down`));
139
+ void shutdown(0);
140
+ });
141
+ }
142
+ }
143
+ async function shutdown(code) {
144
+ stopHeartbeat();
145
+ if (activeSocket) {
146
+ try {
147
+ activeSocket.disconnect();
148
+ }
149
+ catch {
150
+ // ignore
151
+ }
152
+ activeSocket = null;
153
+ }
154
+ process.exit(code);
155
+ }
156
+ //# sourceMappingURL=tunnel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel.js","sourceRoot":"","sources":["../../src/daemon/tunnel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type LLMResponse } from '@redonvn/cli-protocol';
2
+ export type StreamEmitter = (chunk: string) => void;
3
+ /**
4
+ * Tool entry-point `LLMRequest` — generic passthrough sang 8 provider.
5
+ * Hỗ trợ multi-account (round-robin + cooldown) + streaming.
6
+ */
7
+ export declare function llmRequestTool(input: unknown, ctx?: {
8
+ onStream?: StreamEmitter;
9
+ }): Promise<LLMResponse>;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.llmRequestTool = llmRequestTool;
4
+ const cli_protocol_1 = require("@redonvn/cli-protocol");
5
+ const antigravity_1 = require("./providers/antigravity");
6
+ const claude_1 = require("./providers/claude");
7
+ const codex_1 = require("./providers/codex");
8
+ const gemini_1 = require("./providers/gemini");
9
+ const iflow_1 = require("./providers/iflow");
10
+ const kimi_1 = require("./providers/kimi");
11
+ const qwen_1 = require("./providers/qwen");
12
+ const xai_1 = require("./providers/xai");
13
+ const selector_1 = require("./selector");
14
+ const CALL_BY_PROVIDER = {
15
+ claude: claude_1.callClaude,
16
+ codex: codex_1.callCodex,
17
+ gemini: gemini_1.callGemini,
18
+ antigravity: antigravity_1.callAntigravity,
19
+ qwen: qwen_1.callQwen,
20
+ kimi: kimi_1.callKimi,
21
+ iflow: iflow_1.callIFlow,
22
+ xai: xai_1.callXAI,
23
+ };
24
+ const STREAM_BY_PROVIDER = {
25
+ claude: claude_1.streamClaude,
26
+ codex: codex_1.streamCodex,
27
+ gemini: gemini_1.streamGemini,
28
+ antigravity: antigravity_1.streamAntigravity,
29
+ qwen: qwen_1.streamQwen,
30
+ kimi: kimi_1.streamKimi,
31
+ iflow: iflow_1.streamIFlow,
32
+ xai: xai_1.streamXAI,
33
+ };
34
+ /**
35
+ * Tool entry-point `LLMRequest` — generic passthrough sang 8 provider.
36
+ * Hỗ trợ multi-account (round-robin + cooldown) + streaming.
37
+ */
38
+ async function llmRequestTool(input, ctx = {}) {
39
+ const params = cli_protocol_1.LLMRequestParamsSchema.parse(input);
40
+ const account = (0, selector_1.pickAccount)(params.provider, params.account);
41
+ const shared = {
42
+ path: params.path,
43
+ method: params.method,
44
+ body: params.body,
45
+ headers: params.headers,
46
+ account,
47
+ };
48
+ if (params.stream) {
49
+ return runStream(params.provider, shared, ctx.onStream);
50
+ }
51
+ const callFn = CALL_BY_PROVIDER[params.provider];
52
+ if (!callFn)
53
+ throw new Error(`Unknown provider: ${params.provider}`);
54
+ const result = await callFn(shared);
55
+ maybeCooldown(params.provider, account, result.status, result.headers);
56
+ return {
57
+ status: result.status,
58
+ headers: result.headers,
59
+ body: result.body,
60
+ errorType: result.errorType,
61
+ account: result.account,
62
+ };
63
+ }
64
+ async function runStream(provider, shared, onStream) {
65
+ const fn = STREAM_BY_PROVIDER[provider];
66
+ if (!fn)
67
+ throw new Error(`Unknown provider: ${provider}`);
68
+ const iter = fn(shared);
69
+ let final;
70
+ while (true) {
71
+ const next = await iter.next();
72
+ if (next.done) {
73
+ final = next.value;
74
+ break;
75
+ }
76
+ if (onStream)
77
+ onStream(next.value.chunk);
78
+ }
79
+ if (final && shared.account) {
80
+ maybeCooldown(provider, shared.account, final.status, {});
81
+ }
82
+ return {
83
+ status: final?.status ?? 0,
84
+ headers: {},
85
+ body: null,
86
+ account: final?.account,
87
+ };
88
+ }
89
+ function maybeCooldown(provider, account, status, headers) {
90
+ if (status !== 429 && status !== 529)
91
+ return;
92
+ const retryAfterRaw = headers['retry-after'] || headers['Retry-After'];
93
+ const retryAfterSec = retryAfterRaw ? parseInt(retryAfterRaw, 10) : undefined;
94
+ (0, selector_1.markCooldown)(provider, account, {
95
+ retryAfterSec: Number.isFinite(retryAfterSec) ? retryAfterSec : undefined,
96
+ });
97
+ }
98
+ //# sourceMappingURL=llm-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-request.js","sourceRoot":"","sources":["../../src/llm/llm-request.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { OAuthTokenStorage } from './store';
2
+ /**
3
+ * Antigravity OAuth — port từ `internal/auth/antigravity/constants.go`.
4
+ *
5
+ * Giống Gemini ở chỗ dùng Google OAuth chuẩn nhưng client_id riêng + thêm
6
+ * scope `cclog` và `experimentsandconfigs`. API endpoint = cloudcode-pa.
7
+ */
8
+ export declare const ANTIGRAVITY_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
9
+ export declare const ANTIGRAVITY_TOKEN_URL = "https://oauth2.googleapis.com/token";
10
+ export declare const ANTIGRAVITY_USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo?alt=json";
11
+ export declare const ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
12
+ export declare const ANTIGRAVITY_CLIENT_SECRET = "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";
13
+ export declare const ANTIGRAVITY_DEFAULT_CALLBACK_PORT = 51121;
14
+ export declare const ANTIGRAVITY_SCOPES: string[];
15
+ export interface AntigravityAuthSession {
16
+ authUrl: string;
17
+ state: string;
18
+ redirectUri: string;
19
+ }
20
+ export declare function startAntigravityAuth(callbackPort?: number): AntigravityAuthSession;
21
+ export declare function exchangeAntigravityCodeForTokens(code: string, redirectUri: string, account?: string): Promise<OAuthTokenStorage>;
22
+ export declare function refreshAntigravityToken(current: OAuthTokenStorage): Promise<OAuthTokenStorage>;
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ANTIGRAVITY_SCOPES = exports.ANTIGRAVITY_DEFAULT_CALLBACK_PORT = exports.ANTIGRAVITY_CLIENT_SECRET = exports.ANTIGRAVITY_CLIENT_ID = exports.ANTIGRAVITY_USERINFO_URL = exports.ANTIGRAVITY_TOKEN_URL = exports.ANTIGRAVITY_AUTH_URL = void 0;
4
+ exports.startAntigravityAuth = startAntigravityAuth;
5
+ exports.exchangeAntigravityCodeForTokens = exchangeAntigravityCodeForTokens;
6
+ exports.refreshAntigravityToken = refreshAntigravityToken;
7
+ const undici_1 = require("undici");
8
+ const pkce_1 = require("./pkce");
9
+ /**
10
+ * Antigravity OAuth — port từ `internal/auth/antigravity/constants.go`.
11
+ *
12
+ * Giống Gemini ở chỗ dùng Google OAuth chuẩn nhưng client_id riêng + thêm
13
+ * scope `cclog` và `experimentsandconfigs`. API endpoint = cloudcode-pa.
14
+ */
15
+ exports.ANTIGRAVITY_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
16
+ exports.ANTIGRAVITY_TOKEN_URL = 'https://oauth2.googleapis.com/token';
17
+ exports.ANTIGRAVITY_USERINFO_URL = 'https://www.googleapis.com/oauth2/v2/userinfo?alt=json';
18
+ exports.ANTIGRAVITY_CLIENT_ID = '1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com';
19
+ exports.ANTIGRAVITY_CLIENT_SECRET = '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';
20
+ exports.ANTIGRAVITY_DEFAULT_CALLBACK_PORT = 51121;
21
+ exports.ANTIGRAVITY_SCOPES = [
22
+ 'https://www.googleapis.com/auth/cloud-platform',
23
+ 'https://www.googleapis.com/auth/userinfo.email',
24
+ 'https://www.googleapis.com/auth/userinfo.profile',
25
+ 'https://www.googleapis.com/auth/cclog',
26
+ 'https://www.googleapis.com/auth/experimentsandconfigs',
27
+ ];
28
+ function startAntigravityAuth(callbackPort = exports.ANTIGRAVITY_DEFAULT_CALLBACK_PORT) {
29
+ const state = (0, pkce_1.generateRandomState)();
30
+ const redirectUri = `http://localhost:${callbackPort}/oauth2callback`;
31
+ const params = new URLSearchParams({
32
+ client_id: exports.ANTIGRAVITY_CLIENT_ID,
33
+ redirect_uri: redirectUri,
34
+ response_type: 'code',
35
+ scope: exports.ANTIGRAVITY_SCOPES.join(' '),
36
+ state,
37
+ access_type: 'offline',
38
+ prompt: 'consent',
39
+ });
40
+ return {
41
+ authUrl: `${exports.ANTIGRAVITY_AUTH_URL}?${params.toString()}`,
42
+ state,
43
+ redirectUri,
44
+ };
45
+ }
46
+ async function exchangeAntigravityCodeForTokens(code, redirectUri, account = 'default') {
47
+ const data = new URLSearchParams({
48
+ code,
49
+ client_id: exports.ANTIGRAVITY_CLIENT_ID,
50
+ client_secret: exports.ANTIGRAVITY_CLIENT_SECRET,
51
+ redirect_uri: redirectUri,
52
+ grant_type: 'authorization_code',
53
+ });
54
+ const res = await (0, undici_1.request)(exports.ANTIGRAVITY_TOKEN_URL, {
55
+ method: 'POST',
56
+ headers: {
57
+ 'Content-Type': 'application/x-www-form-urlencoded',
58
+ Accept: 'application/json',
59
+ },
60
+ body: data.toString(),
61
+ headersTimeout: 15000,
62
+ bodyTimeout: 30000,
63
+ });
64
+ const text = await res.body.text();
65
+ if (res.statusCode !== 200) {
66
+ throw new Error(`Antigravity token exchange failed (${res.statusCode}): ${text}`);
67
+ }
68
+ const parsed = JSON.parse(text);
69
+ const email = await fetchEmail(parsed.access_token).catch(() => undefined);
70
+ const now = Date.now();
71
+ return {
72
+ type: 'antigravity',
73
+ account,
74
+ accessToken: parsed.access_token,
75
+ refreshToken: parsed.refresh_token ?? '',
76
+ idToken: parsed.id_token,
77
+ email,
78
+ expiresAt: now + parsed.expires_in * 1000,
79
+ lastRefresh: now,
80
+ };
81
+ }
82
+ async function fetchEmail(accessToken) {
83
+ const res = await (0, undici_1.request)(exports.ANTIGRAVITY_USERINFO_URL, {
84
+ method: 'GET',
85
+ headers: { Authorization: `Bearer ${accessToken}` },
86
+ headersTimeout: 10000,
87
+ bodyTimeout: 10000,
88
+ });
89
+ if (res.statusCode !== 200)
90
+ return undefined;
91
+ const info = JSON.parse(await res.body.text());
92
+ return info.email;
93
+ }
94
+ async function refreshAntigravityToken(current) {
95
+ if (!current.refreshToken)
96
+ throw new Error('Antigravity refresh token missing');
97
+ const data = new URLSearchParams({
98
+ client_id: exports.ANTIGRAVITY_CLIENT_ID,
99
+ client_secret: exports.ANTIGRAVITY_CLIENT_SECRET,
100
+ grant_type: 'refresh_token',
101
+ refresh_token: current.refreshToken,
102
+ });
103
+ const res = await (0, undici_1.request)(exports.ANTIGRAVITY_TOKEN_URL, {
104
+ method: 'POST',
105
+ headers: {
106
+ 'Content-Type': 'application/x-www-form-urlencoded',
107
+ Accept: 'application/json',
108
+ },
109
+ body: data.toString(),
110
+ headersTimeout: 15000,
111
+ bodyTimeout: 30000,
112
+ });
113
+ const text = await res.body.text();
114
+ if (res.statusCode !== 200) {
115
+ throw new Error(`Antigravity refresh failed (${res.statusCode}): ${text}`);
116
+ }
117
+ const parsed = JSON.parse(text);
118
+ const now = Date.now();
119
+ return {
120
+ ...current,
121
+ accessToken: parsed.access_token,
122
+ refreshToken: parsed.refresh_token ?? current.refreshToken,
123
+ idToken: parsed.id_token ?? current.idToken,
124
+ expiresAt: now + parsed.expires_in * 1000,
125
+ lastRefresh: now,
126
+ };
127
+ }
128
+ //# sourceMappingURL=antigravity-oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"antigravity-oauth.js","sourceRoot":"","sources":["../../../src/llm/oauth/antigravity-oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface CallbackResult {
2
+ code: string;
3
+ state: string | null;
4
+ }
5
+ /**
6
+ * Khởi 1 HTTP server tạm thời lắng nghe redirect từ provider OAuth.
7
+ * Resolve khi nhận được `code`, reject nếu timeout / lỗi.
8
+ *
9
+ * Phải bind đúng port mà provider whitelist (vd Claude: 54545).
10
+ */
11
+ export declare function waitForCallback(opts: {
12
+ port: number;
13
+ pathname?: string;
14
+ timeoutMs?: number;
15
+ }): {
16
+ promise: Promise<CallbackResult>;
17
+ cancel: () => void;
18
+ };