gm-orchestrator 0.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 (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +289 -0
  3. package/dist/cli/index.d.ts +3 -0
  4. package/dist/cli/index.d.ts.map +1 -0
  5. package/dist/cli/index.js +215 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/core/orchestrator.d.ts +23 -0
  8. package/dist/core/orchestrator.d.ts.map +1 -0
  9. package/dist/core/orchestrator.js +173 -0
  10. package/dist/core/orchestrator.js.map +1 -0
  11. package/dist/core/permissions.d.ts +18 -0
  12. package/dist/core/permissions.d.ts.map +1 -0
  13. package/dist/core/permissions.js +42 -0
  14. package/dist/core/permissions.js.map +1 -0
  15. package/dist/core/prompt-builder.d.ts +25 -0
  16. package/dist/core/prompt-builder.d.ts.map +1 -0
  17. package/dist/core/prompt-builder.js +84 -0
  18. package/dist/core/prompt-builder.js.map +1 -0
  19. package/dist/core/task-utils.d.ts +5 -0
  20. package/dist/core/task-utils.d.ts.map +1 -0
  21. package/dist/core/task-utils.js +27 -0
  22. package/dist/core/task-utils.js.map +1 -0
  23. package/dist/core/types.d.ts +150 -0
  24. package/dist/core/types.d.ts.map +1 -0
  25. package/dist/core/types.js +5 -0
  26. package/dist/core/types.js.map +1 -0
  27. package/dist/index.d.ts +11 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +12 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/infra/claude-runner.d.ts +14 -0
  32. package/dist/infra/claude-runner.d.ts.map +1 -0
  33. package/dist/infra/claude-runner.js +42 -0
  34. package/dist/infra/claude-runner.js.map +1 -0
  35. package/dist/infra/config.d.ts +7 -0
  36. package/dist/infra/config.d.ts.map +1 -0
  37. package/dist/infra/config.js +61 -0
  38. package/dist/infra/config.js.map +1 -0
  39. package/dist/infra/gm-client.d.ts +32 -0
  40. package/dist/infra/gm-client.d.ts.map +1 -0
  41. package/dist/infra/gm-client.js +70 -0
  42. package/dist/infra/gm-client.js.map +1 -0
  43. package/dist/infra/gm-discovery.d.ts +16 -0
  44. package/dist/infra/gm-discovery.d.ts.map +1 -0
  45. package/dist/infra/gm-discovery.js +53 -0
  46. package/dist/infra/gm-discovery.js.map +1 -0
  47. package/dist/infra/logger.d.ts +13 -0
  48. package/dist/infra/logger.d.ts.map +1 -0
  49. package/dist/infra/logger.js +44 -0
  50. package/dist/infra/logger.js.map +1 -0
  51. package/dist/infra/notifications/desktop.d.ts +6 -0
  52. package/dist/infra/notifications/desktop.d.ts.map +1 -0
  53. package/dist/infra/notifications/desktop.js +18 -0
  54. package/dist/infra/notifications/desktop.js.map +1 -0
  55. package/dist/infra/notifications/index.d.ts +9 -0
  56. package/dist/infra/notifications/index.d.ts.map +1 -0
  57. package/dist/infra/notifications/index.js +40 -0
  58. package/dist/infra/notifications/index.js.map +1 -0
  59. package/dist/infra/notifications/telegram.d.ts +9 -0
  60. package/dist/infra/notifications/telegram.d.ts.map +1 -0
  61. package/dist/infra/notifications/telegram.js +41 -0
  62. package/dist/infra/notifications/telegram.js.map +1 -0
  63. package/dist/infra/notifications/types.d.ts +30 -0
  64. package/dist/infra/notifications/types.d.ts.map +1 -0
  65. package/dist/infra/notifications/types.js +3 -0
  66. package/dist/infra/notifications/types.js.map +1 -0
  67. package/dist/infra/notifications/webhook.d.ts +9 -0
  68. package/dist/infra/notifications/webhook.d.ts.map +1 -0
  69. package/dist/infra/notifications/webhook.js +39 -0
  70. package/dist/infra/notifications/webhook.js.map +1 -0
  71. package/dist/infra/task-poller.d.ts +9 -0
  72. package/dist/infra/task-poller.d.ts.map +1 -0
  73. package/dist/infra/task-poller.js +42 -0
  74. package/dist/infra/task-poller.js.map +1 -0
  75. package/dist/server/api.d.ts +23 -0
  76. package/dist/server/api.d.ts.map +1 -0
  77. package/dist/server/api.js +143 -0
  78. package/dist/server/api.js.map +1 -0
  79. package/dist/server/index.d.ts +18 -0
  80. package/dist/server/index.d.ts.map +1 -0
  81. package/dist/server/index.js +73 -0
  82. package/dist/server/index.js.map +1 -0
  83. package/dist/server/runner-service.d.ts +15 -0
  84. package/dist/server/runner-service.d.ts.map +1 -0
  85. package/dist/server/runner-service.js +193 -0
  86. package/dist/server/runner-service.js.map +1 -0
  87. package/dist/server/ws.d.ts +9 -0
  88. package/dist/server/ws.d.ts.map +1 -0
  89. package/dist/server/ws.js +30 -0
  90. package/dist/server/ws.js.map +1 -0
  91. package/dist/ui/assets/index-BYPkFAEX.css +1 -0
  92. package/dist/ui/assets/index-CEQTkIqE.js +60 -0
  93. package/dist/ui/index.html +13 -0
  94. package/package.json +70 -0
@@ -0,0 +1,42 @@
1
+ import { spawn } from 'child_process';
2
+ import { buildPrompt } from '../core/prompt-builder.js';
3
+ export class ClaudeRunner {
4
+ /**
5
+ * Spawns `claude --print <prompt>` as a child process.
6
+ * Streams stdout/stderr to the parent terminal.
7
+ * Resolves when the process exits (for any reason).
8
+ *
9
+ * Note: the orchestrator does NOT rely on process exit for completion
10
+ * detection — it polls GraphMemory task status instead. The session
11
+ * promise is awaited only for cleanup after the poller signals done.
12
+ */
13
+ async run(task, config) {
14
+ const prompt = buildPrompt(task, { projectId: config.projectId });
15
+ const args = ['--print', '--dangerously-skip-permissions', ...config.claudeArgs, prompt];
16
+ return new Promise((resolve, reject) => {
17
+ const proc = spawn('claude', args, {
18
+ stdio: 'inherit',
19
+ env: process.env,
20
+ });
21
+ proc.on('close', (code) => {
22
+ if (code === 0 || code === null) {
23
+ resolve();
24
+ }
25
+ else {
26
+ // Non-zero exit is logged but not fatal — task status drives decisions
27
+ resolve();
28
+ }
29
+ });
30
+ proc.on('error', (err) => {
31
+ if (err.code === 'ENOENT') {
32
+ reject(new Error('claude not found in PATH.\n' +
33
+ 'Install Claude Code: npm install -g @anthropic-ai/claude-code'));
34
+ }
35
+ else {
36
+ reject(err);
37
+ }
38
+ });
39
+ });
40
+ }
41
+ }
42
+ //# sourceMappingURL=claude-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-runner.js","sourceRoot":"","sources":["../../src/infra/claude-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEtC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAExD,MAAM,OAAO,YAAY;IACvB;;;;;;;;OAQG;IACH,KAAK,CAAC,GAAG,CAAC,IAAU,EAAE,MAA0B;QAC9C,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;QAClE,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,gCAAgC,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAEzF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE;gBACjC,KAAK,EAAE,SAAS;gBAChB,GAAG,EAAE,OAAO,CAAC,GAAG;aACjB,CAAC,CAAC;YAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gBACxB,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAChC,OAAO,EAAE,CAAC;gBACZ,CAAC;qBAAM,CAAC;oBACN,uEAAuE;oBACvE,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACvB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACrD,MAAM,CAAC,IAAI,KAAK,CACd,6BAA6B;wBAC7B,+DAA+D,CAChE,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,GAAG,CAAC,CAAC;gBACd,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ import type { OrchestratorConfig } from '../core/types.js';
2
+ export declare function loadConfig(overrides?: Partial<OrchestratorConfig>): OrchestratorConfig;
3
+ export declare function validateConfig(config: OrchestratorConfig): asserts config is OrchestratorConfig & {
4
+ projectId: string;
5
+ };
6
+ export declare function saveConfig(config: Partial<OrchestratorConfig>): void;
7
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/infra/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAc3D,wBAAgB,UAAU,CAAC,SAAS,GAAE,OAAO,CAAC,kBAAkB,CAAM,GAAG,kBAAkB,CAI1F;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,IAAI,kBAAkB,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAkBvH;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI,CAGpE"}
@@ -0,0 +1,61 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
2
+ import { resolve } from 'path';
3
+ const CONFIG_FILE = '.gm-orchestrator.json';
4
+ const DEFAULTS = {
5
+ baseUrl: 'http://localhost:3000',
6
+ projectId: '',
7
+ timeoutMs: 15 * 60 * 1000,
8
+ pauseMs: 2_000,
9
+ maxRetries: 1,
10
+ claudeArgs: [],
11
+ dryRun: false,
12
+ };
13
+ export function loadConfig(overrides = {}) {
14
+ const fileConfig = readFileConfig();
15
+ const envConfig = readEnvConfig();
16
+ return { ...DEFAULTS, ...fileConfig, ...envConfig, ...overrides };
17
+ }
18
+ export function validateConfig(config) {
19
+ const errors = [];
20
+ if (!config.projectId) {
21
+ errors.push('projectId is required.\n' +
22
+ ' Options: --project <id> | GM_PROJECT_ID env | .gm-orchestrator.json');
23
+ }
24
+ if (config.timeoutMs < 10_000) {
25
+ errors.push('timeoutMs must be at least 10000 (10 seconds)');
26
+ }
27
+ if (errors.length) {
28
+ errors.forEach((e) => console.error(`❌ ${e}`));
29
+ process.exit(1);
30
+ }
31
+ }
32
+ export function saveConfig(config) {
33
+ const path = resolve(process.cwd(), CONFIG_FILE);
34
+ writeFileSync(path, JSON.stringify(config, null, 2) + '\n', 'utf8');
35
+ }
36
+ // ── Readers ───────────────────────────────────────────────────────────────
37
+ function readFileConfig() {
38
+ const path = resolve(process.cwd(), CONFIG_FILE);
39
+ if (!existsSync(path))
40
+ return {};
41
+ try {
42
+ return JSON.parse(readFileSync(path, 'utf8'));
43
+ }
44
+ catch {
45
+ console.warn(`Warning: could not parse ${CONFIG_FILE}`);
46
+ return {};
47
+ }
48
+ }
49
+ function readEnvConfig() {
50
+ const cfg = {};
51
+ if (process.env['GM_BASE_URL'])
52
+ cfg.baseUrl = process.env['GM_BASE_URL'];
53
+ if (process.env['GM_PROJECT_ID'])
54
+ cfg.projectId = process.env['GM_PROJECT_ID'];
55
+ if (process.env['GM_API_KEY'])
56
+ cfg.apiKey = process.env['GM_API_KEY'];
57
+ if (process.env['GM_TIMEOUT_MS'])
58
+ cfg.timeoutMs = Number(process.env['GM_TIMEOUT_MS']);
59
+ return cfg;
60
+ }
61
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/infra/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAG/B,MAAM,WAAW,GAAG,uBAAuB,CAAC;AAE5C,MAAM,QAAQ,GAAuB;IACnC,OAAO,EAAE,uBAAuB;IAChC,SAAS,EAAE,EAAE;IACb,SAAS,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IACzB,OAAO,EAAE,KAAK;IACd,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,KAAK;CACd,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,YAAyC,EAAE;IACpE,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,aAAa,EAAE,CAAC;IAClC,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,UAAU,EAAE,GAAG,SAAS,EAAE,GAAG,SAAS,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAA0B;IACvD,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,CAAC,IAAI,CACT,0BAA0B;YAC1B,2EAA2E,CAC5E,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,SAAS,GAAG,MAAM,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;IAC/D,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAmC;IAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;IACjD,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;AACtE,CAAC;AAED,6EAA6E;AAE7E,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAgC,CAAC;IAC/E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,IAAI,CAAC,4BAA4B,WAAW,EAAE,CAAC,CAAC;QACxD,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,GAAG,GAAgC,EAAE,CAAC;IAC5C,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;QAAE,GAAG,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACzE,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;QAAE,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC/E,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAAE,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACtE,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;QAAE,GAAG,CAAC,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;IACvF,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { GraphMemoryPort, Task, TaskStatus, Epic, EpicStatus } from '../core/types.js';
2
+ interface ClientOptions {
3
+ baseUrl: string;
4
+ projectId: string;
5
+ apiKey?: string;
6
+ }
7
+ export declare class GraphMemoryClient implements GraphMemoryPort {
8
+ private readonly base;
9
+ private readonly headers;
10
+ constructor({ baseUrl, projectId, apiKey }: ClientOptions);
11
+ listTasks({ status, tag, limit, }?: {
12
+ status?: TaskStatus;
13
+ tag?: string;
14
+ limit?: number;
15
+ }): Promise<Task[]>;
16
+ getTask(taskId: string): Promise<Task>;
17
+ moveTask(taskId: string, status: TaskStatus): Promise<void>;
18
+ updateTask(taskId: string, fields: Partial<Task>): Promise<void>;
19
+ getEpic(epicId: string): Promise<Epic>;
20
+ listEpics({ status, limit, }?: {
21
+ status?: EpicStatus;
22
+ limit?: number;
23
+ }): Promise<Epic[]>;
24
+ moveEpic(epicId: string, status: EpicStatus): Promise<void>;
25
+ private get;
26
+ private post;
27
+ private put;
28
+ private patch;
29
+ private req;
30
+ }
31
+ export {};
32
+ //# sourceMappingURL=gm-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gm-client.d.ts","sourceRoot":"","sources":["../../src/infra/gm-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,UAAU,EACX,MAAM,kBAAkB,CAAC;AAE1B,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,iBAAkB,YAAW,eAAe;IACvD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;gBAErC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,aAAa;IAUnD,SAAS,CAAC,EACd,MAAM,EACN,GAAG,EACH,KAAW,GACZ,GAAE;QAAE,MAAM,CAAC,EAAE,UAAU,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAQzE,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhE,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,SAAS,CAAC,EACd,MAAM,EACN,KAAU,GACX,GAAE;QAAE,MAAM,CAAC,EAAE,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAO3D,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;YAMnD,GAAG;YAIH,IAAI;YAIJ,GAAG;YAIH,KAAK;YAIL,GAAG;CAclB"}
@@ -0,0 +1,70 @@
1
+ export class GraphMemoryClient {
2
+ base;
3
+ headers;
4
+ constructor({ baseUrl, projectId, apiKey }) {
5
+ this.base = `${baseUrl}/api/projects/${projectId}`;
6
+ this.headers = {
7
+ 'Content-Type': 'application/json',
8
+ ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
9
+ };
10
+ }
11
+ // ── Tasks ─────────────────────────────────────────────────────────────
12
+ async listTasks({ status, tag, limit = 100, } = {}) {
13
+ const params = new URLSearchParams({ limit: String(limit) });
14
+ if (status)
15
+ params.set('status', status);
16
+ if (tag)
17
+ params.set('tag', tag);
18
+ const data = await this.get(`/tasks?${params}`);
19
+ return data.results ?? [];
20
+ }
21
+ async getTask(taskId) {
22
+ return this.get(`/tasks/${taskId}`);
23
+ }
24
+ async moveTask(taskId, status) {
25
+ await this.post(`/tasks/${taskId}/move`, { status });
26
+ }
27
+ async updateTask(taskId, fields) {
28
+ await this.put(`/tasks/${taskId}`, fields);
29
+ }
30
+ // ── Epics ─────────────────────────────────────────────────────────────
31
+ async getEpic(epicId) {
32
+ return this.get(`/epics/${epicId}`);
33
+ }
34
+ async listEpics({ status, limit = 50, } = {}) {
35
+ const params = new URLSearchParams({ limit: String(limit) });
36
+ if (status)
37
+ params.set('status', status);
38
+ const data = await this.get(`/epics?${params}`);
39
+ return data.results ?? [];
40
+ }
41
+ async moveEpic(epicId, status) {
42
+ await this.put(`/epics/${epicId}`, { status });
43
+ }
44
+ // ── HTTP ──────────────────────────────────────────────────────────────
45
+ async get(path) {
46
+ return this.req('GET', path);
47
+ }
48
+ async post(path, body) {
49
+ return this.req('POST', path, body);
50
+ }
51
+ async put(path, body) {
52
+ return this.req('PUT', path, body);
53
+ }
54
+ async patch(path, body) {
55
+ return this.req('PATCH', path, body);
56
+ }
57
+ async req(method, path, body) {
58
+ const res = await fetch(`${this.base}${path}`, {
59
+ method,
60
+ headers: this.headers,
61
+ body: body !== undefined ? JSON.stringify(body) : null,
62
+ });
63
+ if (!res.ok) {
64
+ const text = await res.text().catch(() => '');
65
+ throw new Error(`GraphMemory ${method} ${path} → HTTP ${res.status}: ${text}`);
66
+ }
67
+ return res.json();
68
+ }
69
+ }
70
+ //# sourceMappingURL=gm-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gm-client.js","sourceRoot":"","sources":["../../src/infra/gm-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export interface GMServerProject {
2
+ id: string;
3
+ taskCount: number;
4
+ epicCount: number;
5
+ }
6
+ export interface GMServer {
7
+ url: string;
8
+ port: number;
9
+ projects: GMServerProject[];
10
+ }
11
+ /**
12
+ * Auto-discover GraphMemory servers on localhost ports 3000–3010.
13
+ * Scans all ports in parallel and returns responding servers.
14
+ */
15
+ export declare function discoverServers(): Promise<GMServer[]>;
16
+ //# sourceMappingURL=gm-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gm-discovery.d.ts","sourceRoot":"","sources":["../../src/infra/gm-discovery.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AA6CD;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAe3D"}
@@ -0,0 +1,53 @@
1
+ // ─── GraphMemory Server Discovery ─────────────────────────────────────────
2
+ // Scans localhost ports 3000–3010 to find running GraphMemory instances.
3
+ const PORT_START = 3000;
4
+ const PORT_END = 3010;
5
+ const TIMEOUT_MS = 500;
6
+ /**
7
+ * Probe a single port for a running GraphMemory server.
8
+ * Tries GET /api/projects first; falls back to GET /api/health.
9
+ */
10
+ async function probePort(port) {
11
+ const base = `http://127.0.0.1:${port}`;
12
+ try {
13
+ const res = await fetch(`${base}/api/projects`, {
14
+ signal: AbortSignal.timeout(TIMEOUT_MS),
15
+ });
16
+ if (res.ok) {
17
+ const data = (await res.json());
18
+ return {
19
+ url: base,
20
+ port,
21
+ projects: data.results ?? [],
22
+ };
23
+ }
24
+ }
25
+ catch {
26
+ // /api/projects failed — try health endpoint
27
+ }
28
+ try {
29
+ const res = await fetch(`${base}/api/health`, {
30
+ signal: AbortSignal.timeout(TIMEOUT_MS),
31
+ });
32
+ if (res.ok) {
33
+ return { url: base, port, projects: [] };
34
+ }
35
+ }
36
+ catch {
37
+ // not a GM server on this port
38
+ }
39
+ return null;
40
+ }
41
+ /**
42
+ * Auto-discover GraphMemory servers on localhost ports 3000–3010.
43
+ * Scans all ports in parallel and returns responding servers.
44
+ */
45
+ export async function discoverServers() {
46
+ const ports = Array.from({ length: PORT_END - PORT_START + 1 }, (_, i) => PORT_START + i);
47
+ const results = await Promise.allSettled(ports.map(probePort));
48
+ return results
49
+ .filter((r) => r.status === 'fulfilled')
50
+ .map((r) => r.value)
51
+ .filter((server) => server !== null);
52
+ }
53
+ //# sourceMappingURL=gm-discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gm-discovery.js","sourceRoot":"","sources":["../../src/infra/gm-discovery.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,yEAAyE;AAczE,MAAM,UAAU,GAAG,IAAI,CAAC;AACxB,MAAM,QAAQ,GAAG,IAAI,CAAC;AACtB,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB;;;GAGG;AACH,KAAK,UAAU,SAAS,CAAC,IAAY;IACnC,MAAM,IAAI,GAAG,oBAAoB,IAAI,EAAE,CAAC;IAExC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,eAAe,EAAE;YAC9C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;SACxC,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAoC,CAAC;YACnE,OAAO;gBACL,GAAG,EAAE,IAAI;gBACT,IAAI;gBACJ,QAAQ,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE;aAC7B,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6CAA6C;IAC/C,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,aAAa,EAAE;YAC5C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;SACxC,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;YACX,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QAC3C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CACtB,EAAE,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,CAAC,EAAE,EACrC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,UAAU,GAAG,CAAC,CACzB,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IAE/D,OAAO,OAAO;SACX,MAAM,CACL,CAAC,CAAC,EAAgD,EAAE,CAClD,CAAC,CAAC,MAAM,KAAK,WAAW,CAC3B;SACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;SACnB,MAAM,CAAC,CAAC,MAAM,EAAsB,EAAE,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { Task } from '../core/types.js';
2
+ export interface Logger {
3
+ info(msg: string): void;
4
+ success(msg: string): void;
5
+ warn(msg: string): void;
6
+ error(msg: string): void;
7
+ skip(msg: string): void;
8
+ section(msg: string): void;
9
+ task(task: Task): void;
10
+ }
11
+ export declare const consoleLogger: Logger;
12
+ export declare const silentLogger: Logger;
13
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/infra/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAM7C,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AA0BD,eAAO,MAAM,aAAa,EAAE,MAe3B,CAAC;AAIF,eAAO,MAAM,YAAY,EAAE,MAQ1B,CAAC"}
@@ -0,0 +1,44 @@
1
+ // ── Console implementation ────────────────────────────────────────────────
2
+ const C = {
3
+ reset: '\x1b[0m',
4
+ bold: '\x1b[1m',
5
+ dim: '\x1b[2m',
6
+ red: '\x1b[31m',
7
+ green: '\x1b[32m',
8
+ yellow: '\x1b[33m',
9
+ cyan: '\x1b[36m',
10
+ magenta: '\x1b[35m',
11
+ };
12
+ function ts() {
13
+ return `${C.dim}${new Date().toTimeString().slice(0, 8)}${C.reset}`;
14
+ }
15
+ const PRIORITY_COLOR = {
16
+ critical: C.red,
17
+ high: C.yellow,
18
+ medium: C.cyan,
19
+ low: C.dim,
20
+ };
21
+ export const consoleLogger = {
22
+ info: (msg) => console.log(`${ts()} ${C.cyan}●${C.reset} ${msg}`),
23
+ success: (msg) => console.log(`${ts()} ${C.green}✓${C.reset} ${msg}`),
24
+ warn: (msg) => console.log(`${ts()} ${C.yellow}⚠${C.reset} ${msg}`),
25
+ error: (msg) => console.log(`${ts()} ${C.red}✗${C.reset} ${msg}`),
26
+ skip: (msg) => console.log(`${ts()} ${C.dim}→ ${msg}${C.reset}`),
27
+ section: (msg) => console.log(`\n${C.bold}${C.magenta}━━ ${msg} ━━${C.reset}`),
28
+ task: (task) => {
29
+ const pc = PRIORITY_COLOR[task.priority] ?? C.reset;
30
+ console.log(`${ts()} ${pc}▶${C.reset} [${task.priority.padEnd(8)}] ${task.title}`);
31
+ console.log(`${C.dim} id: ${task.id}${C.reset}`);
32
+ },
33
+ };
34
+ // ── Silent logger for tests ───────────────────────────────────────────────
35
+ export const silentLogger = {
36
+ info: () => { },
37
+ success: () => { },
38
+ warn: () => { },
39
+ error: () => { },
40
+ skip: () => { },
41
+ section: () => { },
42
+ task: () => { },
43
+ };
44
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/infra/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { NotificationPayload, NotificationPort } from './types.js';
2
+ export declare class DesktopNotifier implements NotificationPort {
3
+ send(payload: NotificationPayload): Promise<void>;
4
+ test(): Promise<void>;
5
+ }
6
+ //# sourceMappingURL=desktop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop.d.ts","sourceRoot":"","sources":["../../../src/infra/notifications/desktop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAExE,qBAAa,eAAgB,YAAW,gBAAgB;IAChD,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IASjD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAO5B"}
@@ -0,0 +1,18 @@
1
+ export class DesktopNotifier {
2
+ async send(payload) {
3
+ const notifier = await import('node-notifier');
4
+ notifier.default.notify({
5
+ title: payload.title,
6
+ message: payload.body,
7
+ sound: true,
8
+ });
9
+ }
10
+ async test() {
11
+ await this.send({
12
+ event: 'test',
13
+ title: 'gm-orchestrator connected',
14
+ body: 'Desktop notifications are working.',
15
+ });
16
+ }
17
+ }
18
+ //# sourceMappingURL=desktop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop.js","sourceRoot":"","sources":["../../../src/infra/notifications/desktop.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,eAAe;IAC1B,KAAK,CAAC,IAAI,CAAC,OAA4B;QACrC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;QAC/C,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC;YACtB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,OAAO,EAAE,OAAO,CAAC,IAAI;YACrB,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,IAAI,CAAC;YACd,KAAK,EAAE,MAAM;YACb,KAAK,EAAE,2BAA2B;YAClC,IAAI,EAAE,oCAAoC;SAC3C,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import type { Logger } from '../logger.js';
2
+ import type { NotificationConfig, NotificationPayload } from './types.js';
3
+ export type { NotificationConfig, NotificationPayload, NotificationPort } from './types.js';
4
+ export interface NotificationDispatcher {
5
+ dispatch(payload: NotificationPayload): Promise<void>;
6
+ testAll(): Promise<void>;
7
+ }
8
+ export declare function createNotifier(config: NotificationConfig, logger: Logger): NotificationDispatcher;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/infra/notifications/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAoB,MAAM,YAAY,CAAC;AAK5F,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE5F,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,MAAM,GACb,sBAAsB,CAwCxB"}
@@ -0,0 +1,40 @@
1
+ import { TelegramNotifier } from './telegram.js';
2
+ import { WebhookNotifier } from './webhook.js';
3
+ import { DesktopNotifier } from './desktop.js';
4
+ export function createNotifier(config, logger) {
5
+ const channels = [];
6
+ if (config.telegram?.enabled) {
7
+ channels.push({ name: 'telegram', port: new TelegramNotifier(config.telegram) });
8
+ }
9
+ if (config.webhook?.enabled) {
10
+ channels.push({ name: 'webhook', port: new WebhookNotifier(config.webhook) });
11
+ }
12
+ if (config.desktop?.enabled) {
13
+ channels.push({ name: 'desktop', port: new DesktopNotifier() });
14
+ }
15
+ return {
16
+ async dispatch(payload) {
17
+ if (channels.length === 0)
18
+ return;
19
+ const results = await Promise.allSettled(channels.map(({ port }) => port.send(payload)));
20
+ results.forEach((result, i) => {
21
+ if (result.status === 'rejected') {
22
+ const ch = channels[i];
23
+ logger.warn(`Notification [${ch.name}] failed: ${result.reason}`);
24
+ }
25
+ });
26
+ },
27
+ async testAll() {
28
+ for (const { name, port } of channels) {
29
+ try {
30
+ await port.test();
31
+ logger.success(`Notification [${name}] test OK`);
32
+ }
33
+ catch (err) {
34
+ logger.warn(`Notification [${name}] test failed: ${err}`);
35
+ }
36
+ }
37
+ },
38
+ };
39
+ }
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infra/notifications/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAS/C,MAAM,UAAU,cAAc,CAC5B,MAA0B,EAC1B,MAAc;IAEd,MAAM,QAAQ,GAAoD,EAAE,CAAC;IAErE,IAAI,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;QAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,eAAe,EAAE,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO;QACL,KAAK,CAAC,QAAQ,CAAC,OAA4B;YACzC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YAElC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACtC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAC/C,CAAC;YAEF,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBAC5B,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;oBACjC,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;oBACxB,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,IAAI,aAAa,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBACpE,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,OAAO;YACX,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,QAAQ,EAAE,CAAC;gBACtC,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;oBAClB,MAAM,CAAC,OAAO,CAAC,iBAAiB,IAAI,WAAW,CAAC,CAAC;gBACnD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,IAAI,CAAC,iBAAiB,IAAI,kBAAkB,GAAG,EAAE,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { NotificationPayload, NotificationPort, TelegramConfig } from './types.js';
2
+ export declare class TelegramNotifier implements NotificationPort {
3
+ private readonly apiUrl;
4
+ private readonly chatId;
5
+ constructor(config: TelegramConfig);
6
+ send(payload: NotificationPayload): Promise<void>;
7
+ test(): Promise<void>;
8
+ }
9
+ //# sourceMappingURL=telegram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../../src/infra/notifications/telegram.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAWxF,qBAAa,gBAAiB,YAAW,gBAAgB;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,cAAc;IAK5B,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBjD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAO5B"}
@@ -0,0 +1,41 @@
1
+ const EVENT_EMOJI = {
2
+ task_done: '✅',
3
+ task_failed: '❌',
4
+ sprint_complete: '🏁',
5
+ epic_complete: '🎯',
6
+ error: '🚨',
7
+ test: '🔔',
8
+ };
9
+ export class TelegramNotifier {
10
+ apiUrl;
11
+ chatId;
12
+ constructor(config) {
13
+ this.apiUrl = `https://api.telegram.org/bot${config.botToken}/sendMessage`;
14
+ this.chatId = config.chatId;
15
+ }
16
+ async send(payload) {
17
+ const emoji = EVENT_EMOJI[payload.event] ?? '📋';
18
+ const text = `${emoji} *${payload.title}*\n\n${payload.body}`;
19
+ const res = await fetch(this.apiUrl, {
20
+ method: 'POST',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: JSON.stringify({
23
+ chat_id: this.chatId,
24
+ text,
25
+ parse_mode: 'Markdown',
26
+ }),
27
+ });
28
+ if (!res.ok) {
29
+ const body = await res.text();
30
+ throw new Error(`Telegram API error ${res.status}: ${body}`);
31
+ }
32
+ }
33
+ async test() {
34
+ await this.send({
35
+ event: 'test',
36
+ title: 'gm-orchestrator connected',
37
+ body: '🔔 Telegram notifications are working.',
38
+ });
39
+ }
40
+ }
41
+ //# sourceMappingURL=telegram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../../src/infra/notifications/telegram.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAA2B;IAC1C,SAAS,EAAE,GAAG;IACd,WAAW,EAAE,GAAG;IAChB,eAAe,EAAE,IAAI;IACrB,aAAa,EAAE,IAAI;IACnB,KAAK,EAAE,IAAI;IACX,IAAI,EAAE,IAAI;CACX,CAAC;AAEF,MAAM,OAAO,gBAAgB;IACV,MAAM,CAAS;IACf,MAAM,CAAS;IAEhC,YAAY,MAAsB;QAChC,IAAI,CAAC,MAAM,GAAG,+BAA+B,MAAM,CAAC,QAAQ,cAAc,CAAC;QAC3E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAA4B;QACrC,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;QACjD,MAAM,IAAI,GAAG,GAAG,KAAK,KAAK,OAAO,CAAC,KAAK,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QAE9D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE;YACnC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,OAAO,EAAE,IAAI,CAAC,MAAM;gBACpB,IAAI;gBACJ,UAAU,EAAE,UAAU;aACvB,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,IAAI,CAAC;YACd,KAAK,EAAE,MAAM;YACb,KAAK,EAAE,2BAA2B;YAClC,IAAI,EAAE,wCAAwC;SAC/C,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,30 @@
1
+ export type NotificationEvent = 'task_done' | 'task_failed' | 'sprint_complete' | 'epic_complete' | 'error' | 'test';
2
+ export interface NotificationPayload {
3
+ event: NotificationEvent;
4
+ title: string;
5
+ body: string;
6
+ data?: Record<string, unknown>;
7
+ }
8
+ export interface NotificationPort {
9
+ send(payload: NotificationPayload): Promise<void>;
10
+ test(): Promise<void>;
11
+ }
12
+ export interface TelegramConfig {
13
+ enabled: boolean;
14
+ botToken: string;
15
+ chatId: string;
16
+ }
17
+ export interface WebhookConfig {
18
+ enabled: boolean;
19
+ url: string;
20
+ headers?: Record<string, string>;
21
+ }
22
+ export interface DesktopConfig {
23
+ enabled: boolean;
24
+ }
25
+ export interface NotificationConfig {
26
+ telegram?: TelegramConfig;
27
+ webhook?: WebhookConfig;
28
+ desktop?: DesktopConfig;
29
+ }
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/infra/notifications/types.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GACzB,WAAW,GACX,aAAa,GACb,iBAAiB,GACjB,eAAe,GACf,OAAO,GACP,MAAM,CAAC;AAEX,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAID,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB"}
@@ -0,0 +1,3 @@
1
+ // ─── Notification Types ──────────────────────────────────────────────────
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/infra/notifications/types.ts"],"names":[],"mappings":"AAAA,4EAA4E"}
@@ -0,0 +1,9 @@
1
+ import type { NotificationPayload, NotificationPort, WebhookConfig } from './types.js';
2
+ export declare class WebhookNotifier implements NotificationPort {
3
+ private readonly url;
4
+ private readonly headers;
5
+ constructor(config: WebhookConfig);
6
+ send(payload: NotificationPayload): Promise<void>;
7
+ test(): Promise<void>;
8
+ }
9
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../../src/infra/notifications/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEvF,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;gBAErC,MAAM,EAAE,aAAa;IAQ3B,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBjD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAO5B"}