@intrect/openswarm 0.4.4 → 0.8.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.
- package/README.md +46 -26
- package/config.example.yaml +22 -10
- package/dist/adapters/agenticLoop.d.ts +45 -2
- package/dist/adapters/agenticLoop.d.ts.map +1 -1
- package/dist/adapters/agenticLoop.js +209 -61
- package/dist/adapters/agenticLoop.js.map +1 -1
- package/dist/adapters/chatStream.d.ts +55 -0
- package/dist/adapters/chatStream.d.ts.map +1 -0
- package/dist/adapters/chatStream.js +116 -0
- package/dist/adapters/chatStream.js.map +1 -0
- package/dist/adapters/codex.d.ts +7 -0
- package/dist/adapters/codex.d.ts.map +1 -1
- package/dist/adapters/codex.js +67 -6
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/codexModels.d.ts +20 -0
- package/dist/adapters/codexModels.d.ts.map +1 -0
- package/dist/adapters/codexModels.js +195 -0
- package/dist/adapters/codexModels.js.map +1 -0
- package/dist/adapters/codexResponses.d.ts +97 -0
- package/dist/adapters/codexResponses.d.ts.map +1 -0
- package/dist/adapters/codexResponses.js +302 -0
- package/dist/adapters/codexResponses.js.map +1 -0
- package/dist/adapters/gpt.d.ts.map +1 -1
- package/dist/adapters/gpt.js +20 -120
- package/dist/adapters/gpt.js.map +1 -1
- package/dist/adapters/index.d.ts +10 -2
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +19 -5
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/lmstudio.d.ts +8 -0
- package/dist/adapters/lmstudio.d.ts.map +1 -0
- package/dist/adapters/lmstudio.js +41 -0
- package/dist/adapters/lmstudio.js.map +1 -0
- package/dist/adapters/local.d.ts +16 -1
- package/dist/adapters/local.d.ts.map +1 -1
- package/dist/adapters/local.js +51 -127
- package/dist/adapters/local.js.map +1 -1
- package/dist/adapters/openrouter.d.ts +37 -0
- package/dist/adapters/openrouter.d.ts.map +1 -0
- package/dist/adapters/openrouter.js +193 -0
- package/dist/adapters/openrouter.js.map +1 -0
- package/dist/adapters/resultParsing.d.ts +6 -0
- package/dist/adapters/resultParsing.d.ts.map +1 -0
- package/dist/adapters/resultParsing.js +139 -0
- package/dist/adapters/resultParsing.js.map +1 -0
- package/dist/adapters/tools.d.ts +21 -2
- package/dist/adapters/tools.d.ts.map +1 -1
- package/dist/adapters/tools.js +153 -12
- package/dist/adapters/tools.js.map +1 -1
- package/dist/adapters/types.d.ts +40 -1
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/webTools.d.ts +9 -0
- package/dist/adapters/webTools.d.ts.map +1 -0
- package/dist/adapters/webTools.js +195 -0
- package/dist/adapters/webTools.js.map +1 -0
- package/dist/agents/draftAnalyzer.d.ts +1 -1
- package/dist/agents/draftAnalyzer.d.ts.map +1 -1
- package/dist/agents/draftAnalyzer.js +3 -3
- package/dist/agents/draftAnalyzer.js.map +1 -1
- package/dist/agents/pairPipeline.d.ts +26 -1
- package/dist/agents/pairPipeline.d.ts.map +1 -1
- package/dist/agents/pairPipeline.js +184 -17
- package/dist/agents/pairPipeline.js.map +1 -1
- package/dist/agents/reflection.d.ts +56 -0
- package/dist/agents/reflection.d.ts.map +1 -0
- package/dist/agents/reflection.js +120 -0
- package/dist/agents/reflection.js.map +1 -0
- package/dist/agents/worker.d.ts +8 -0
- package/dist/agents/worker.d.ts.map +1 -1
- package/dist/agents/worker.js +23 -2
- package/dist/agents/worker.js.map +1 -1
- package/dist/auth/index.d.ts +3 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/linearPkce.d.ts +22 -0
- package/dist/auth/linearPkce.d.ts.map +1 -0
- package/dist/auth/linearPkce.js +208 -0
- package/dist/auth/linearPkce.js.map +1 -0
- package/dist/auth/oauthPkce.d.ts +4 -3
- package/dist/auth/oauthPkce.d.ts.map +1 -1
- package/dist/auth/oauthPkce.js +39 -12
- package/dist/auth/oauthPkce.js.map +1 -1
- package/dist/auth/oauthStore.d.ts +11 -1
- package/dist/auth/oauthStore.d.ts.map +1 -1
- package/dist/auth/oauthStore.js +14 -2
- package/dist/auth/oauthStore.js.map +1 -1
- package/dist/auth/openrouterPkce.d.ts +29 -0
- package/dist/auth/openrouterPkce.d.ts.map +1 -0
- package/dist/auth/openrouterPkce.js +222 -0
- package/dist/auth/openrouterPkce.js.map +1 -0
- package/dist/automation/autonomousRunner.d.ts +17 -7
- package/dist/automation/autonomousRunner.d.ts.map +1 -1
- package/dist/automation/autonomousRunner.js +69 -70
- package/dist/automation/autonomousRunner.js.map +1 -1
- package/dist/automation/backlogGrooming.d.ts +21 -0
- package/dist/automation/backlogGrooming.d.ts.map +1 -0
- package/dist/automation/backlogGrooming.js +80 -0
- package/dist/automation/backlogGrooming.js.map +1 -0
- package/dist/automation/localCI.d.ts +24 -0
- package/dist/automation/localCI.d.ts.map +1 -0
- package/dist/automation/localCI.js +84 -0
- package/dist/automation/localCI.js.map +1 -0
- package/dist/automation/runnerExecution.d.ts +33 -7
- package/dist/automation/runnerExecution.d.ts.map +1 -1
- package/dist/automation/runnerExecution.js +202 -154
- package/dist/automation/runnerExecution.js.map +1 -1
- package/dist/automation/runnerTypes.d.ts +5 -1
- package/dist/automation/runnerTypes.d.ts.map +1 -1
- package/dist/automation/taskSource.d.ts +101 -0
- package/dist/automation/taskSource.d.ts.map +1 -0
- package/dist/automation/taskSource.js +136 -0
- package/dist/automation/taskSource.js.map +1 -0
- package/dist/automation/workerAuditLog.d.ts +29 -0
- package/dist/automation/workerAuditLog.d.ts.map +1 -0
- package/dist/automation/workerAuditLog.js +82 -0
- package/dist/automation/workerAuditLog.js.map +1 -0
- package/dist/cli/authHandler.d.ts +14 -5
- package/dist/cli/authHandler.d.ts.map +1 -1
- package/dist/cli/authHandler.js +131 -36
- package/dist/cli/authHandler.js.map +1 -1
- package/dist/cli/daemon.d.ts +5 -0
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +13 -0
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/doctorHandler.d.ts +2 -0
- package/dist/cli/doctorHandler.d.ts.map +1 -0
- package/dist/cli/doctorHandler.js +114 -0
- package/dist/cli/doctorHandler.js.map +1 -0
- package/dist/cli/initWizard.d.ts +13 -0
- package/dist/cli/initWizard.d.ts.map +1 -0
- package/dist/cli/initWizard.js +314 -0
- package/dist/cli/initWizard.js.map +1 -0
- package/dist/cli.js +112 -38
- package/dist/cli.js.map +1 -1
- package/dist/core/config.d.ts +89 -15
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +100 -23
- package/dist/core/config.js.map +1 -1
- package/dist/core/envFile.d.ts +6 -0
- package/dist/core/envFile.d.ts.map +1 -1
- package/dist/core/envFile.js +39 -1
- package/dist/core/envFile.js.map +1 -1
- package/dist/core/eventHub.d.ts +22 -0
- package/dist/core/eventHub.d.ts.map +1 -1
- package/dist/core/eventHub.js.map +1 -1
- package/dist/core/providerOverride.d.ts +6 -0
- package/dist/core/providerOverride.d.ts.map +1 -0
- package/dist/core/providerOverride.js +37 -0
- package/dist/core/providerOverride.js.map +1 -0
- package/dist/core/service.d.ts.map +1 -1
- package/dist/core/service.js +43 -20
- package/dist/core/service.js.map +1 -1
- package/dist/core/types.d.ts +16 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/discord/discordCore.d.ts.map +1 -1
- package/dist/discord/discordCore.js +16 -103
- package/dist/discord/discordCore.js.map +1 -1
- package/dist/discord/discordHandlers.d.ts.map +1 -1
- package/dist/discord/discordHandlers.js +15 -12
- package/dist/discord/discordHandlers.js.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/issues/sqliteStore.d.ts.map +1 -1
- package/dist/issues/sqliteStore.js +7 -2
- package/dist/issues/sqliteStore.js.map +1 -1
- package/dist/knowledge/scanner.d.ts.map +1 -1
- package/dist/knowledge/scanner.js +5 -0
- package/dist/knowledge/scanner.js.map +1 -1
- package/dist/linear/linear.d.ts +45 -2
- package/dist/linear/linear.d.ts.map +1 -1
- package/dist/linear/linear.js +154 -3
- package/dist/linear/linear.js.map +1 -1
- package/dist/locale/prompts/en.d.ts.map +1 -1
- package/dist/locale/prompts/en.js +40 -20
- package/dist/locale/prompts/en.js.map +1 -1
- package/dist/locale/prompts/ko.d.ts.map +1 -1
- package/dist/locale/prompts/ko.js +39 -20
- package/dist/locale/prompts/ko.js.map +1 -1
- package/dist/locale/types.d.ts +6 -0
- package/dist/locale/types.d.ts.map +1 -1
- package/dist/mcp/mcpClient.d.ts +27 -0
- package/dist/mcp/mcpClient.d.ts.map +1 -0
- package/dist/mcp/mcpClient.js +145 -0
- package/dist/mcp/mcpClient.js.map +1 -0
- package/dist/memory/memoryCore.d.ts.map +1 -1
- package/dist/memory/memoryCore.js +8 -2
- package/dist/memory/memoryCore.js.map +1 -1
- package/dist/memory/repoKnowledge.d.ts +33 -0
- package/dist/memory/repoKnowledge.d.ts.map +1 -0
- package/dist/memory/repoKnowledge.js +97 -0
- package/dist/memory/repoKnowledge.js.map +1 -0
- package/dist/notify/notifier.d.ts +26 -0
- package/dist/notify/notifier.d.ts.map +1 -0
- package/dist/notify/notifier.js +131 -0
- package/dist/notify/notifier.js.map +1 -0
- package/dist/orchestration/conflictDetector.d.ts +5 -2
- package/dist/orchestration/conflictDetector.d.ts.map +1 -1
- package/dist/orchestration/conflictDetector.js +35 -6
- package/dist/orchestration/conflictDetector.js.map +1 -1
- package/dist/orchestration/decisionEngine.d.ts +20 -0
- package/dist/orchestration/decisionEngine.d.ts.map +1 -1
- package/dist/orchestration/decisionEngine.js +45 -0
- package/dist/orchestration/decisionEngine.js.map +1 -1
- package/dist/support/banner.d.ts +7 -0
- package/dist/support/banner.d.ts.map +1 -0
- package/dist/support/banner.js +32 -0
- package/dist/support/banner.js.map +1 -0
- package/dist/support/chat.js +36 -27
- package/dist/support/chat.js.map +1 -1
- package/dist/support/chatBackend.d.ts +6 -0
- package/dist/support/chatBackend.d.ts.map +1 -1
- package/dist/support/chatBackend.js +105 -83
- package/dist/support/chatBackend.js.map +1 -1
- package/dist/support/chatTui.d.ts.map +1 -1
- package/dist/support/chatTui.js +217 -27
- package/dist/support/chatTui.js.map +1 -1
- package/dist/support/colors.d.ts +15 -0
- package/dist/support/colors.d.ts.map +1 -0
- package/dist/support/colors.js +23 -0
- package/dist/support/colors.js.map +1 -0
- package/dist/support/dashboardHtml.d.ts +1 -1
- package/dist/support/dashboardHtml.d.ts.map +1 -1
- package/dist/support/dashboardHtml.js +318 -65
- package/dist/support/dashboardHtml.js.map +1 -1
- package/dist/support/planCommand.d.ts +17 -0
- package/dist/support/planCommand.d.ts.map +1 -0
- package/dist/support/planCommand.js +114 -0
- package/dist/support/planCommand.js.map +1 -0
- package/dist/support/planner.d.ts +9 -2
- package/dist/support/planner.d.ts.map +1 -1
- package/dist/support/planner.js +43 -111
- package/dist/support/planner.js.map +1 -1
- package/dist/support/projectMapper.d.ts.map +1 -1
- package/dist/support/projectMapper.js +29 -1
- package/dist/support/projectMapper.js.map +1 -1
- package/dist/support/promptHelper.d.ts +24 -0
- package/dist/support/promptHelper.d.ts.map +1 -0
- package/dist/support/promptHelper.js +94 -0
- package/dist/support/promptHelper.js.map +1 -0
- package/dist/support/rateLimiter.d.ts.map +1 -1
- package/dist/support/rateLimiter.js +6 -0
- package/dist/support/rateLimiter.js.map +1 -1
- package/dist/support/repoMetadata.d.ts +51 -0
- package/dist/support/repoMetadata.d.ts.map +1 -0
- package/dist/support/repoMetadata.js +91 -0
- package/dist/support/repoMetadata.js.map +1 -0
- package/dist/support/web.d.ts.map +1 -1
- package/dist/support/web.js +196 -71
- package/dist/support/web.js.map +1 -1
- package/dist/support/worktreeManager.d.ts +6 -1
- package/dist/support/worktreeManager.d.ts.map +1 -1
- package/dist/support/worktreeManager.js +23 -1
- package/dist/support/worktreeManager.js.map +1 -1
- package/dist/taskState/store.d.ts +1 -0
- package/dist/taskState/store.d.ts.map +1 -1
- package/dist/taskState/store.js +22 -1
- package/dist/taskState/store.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// ============================================
|
|
2
|
+
// OpenSwarm - Self-Repair Reflection
|
|
3
|
+
// Bad-edit guard + bounded reflection loop
|
|
4
|
+
// ============================================
|
|
5
|
+
//
|
|
6
|
+
// claude -p (and most CLI coding agents) lack the native sub-agent / structured
|
|
7
|
+
// self-repair that the first-party Claude harness has. When an edit breaks the
|
|
8
|
+
// build, the agent often reports success anyway. This module gives the pipeline a
|
|
9
|
+
// lightweight self-repair loop, modeled on two proven techniques:
|
|
10
|
+
//
|
|
11
|
+
// - SWE-agent "bad-edit guard": an edit that fails lint is treated as invalid;
|
|
12
|
+
// the concrete lint error is fed straight back so the next attempt fixes it
|
|
13
|
+
// (ablation: removing it dropped resolve rate 18% → 15%).
|
|
14
|
+
// - Aider "reflection": lint/test errors from the previous attempt are preserved
|
|
15
|
+
// into the next prompt, up to a bounded reflection count (default 3).
|
|
16
|
+
//
|
|
17
|
+
// The worker here is an autonomous CLI process, so we cannot intercept a single
|
|
18
|
+
// tool-edit to discard it the way SWE-agent does. The equivalent is: lint the
|
|
19
|
+
// changed files after the worker finishes, and on failure drive a bounded retry
|
|
20
|
+
// with the exact error preserved — objective errors survive even a fresh-context
|
|
21
|
+
// reset, because unlike a reviewer's subjective opinion a lint/test failure is
|
|
22
|
+
// ground truth worth carrying forward.
|
|
23
|
+
// Constants
|
|
24
|
+
/** Default cap on objective self-repair attempts before giving up. */
|
|
25
|
+
export const DEFAULT_MAX_REFLECTIONS = 3;
|
|
26
|
+
/** Keep only the most recent N objective entries in the prompt (avoid bloat). */
|
|
27
|
+
export const MAX_TRAIL_ENTRIES = 3;
|
|
28
|
+
/** Cap error lines per entry so a chatty compiler cannot blow up the prompt. */
|
|
29
|
+
const MAX_ERRORS_PER_ENTRY = 5;
|
|
30
|
+
const OBJECTIVE_SOURCES = ['lint', 'bs', 'test'];
|
|
31
|
+
// State
|
|
32
|
+
export function createReflectionState() {
|
|
33
|
+
return { entries: [], reflectionCount: 0 };
|
|
34
|
+
}
|
|
35
|
+
/** True for ground-truth failures that drive (and bound) self-repair. */
|
|
36
|
+
export function isObjective(source) {
|
|
37
|
+
return OBJECTIVE_SOURCES.includes(source);
|
|
38
|
+
}
|
|
39
|
+
/** The most recent objective entry, or undefined if none yet. */
|
|
40
|
+
function lastObjectiveEntry(state) {
|
|
41
|
+
for (let i = state.entries.length - 1; i >= 0; i--) {
|
|
42
|
+
if (isObjective(state.entries[i].source))
|
|
43
|
+
return state.entries[i];
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
/** Two error lists are "the same failure" when their trimmed lines match exactly. */
|
|
48
|
+
function sameErrors(a, b) {
|
|
49
|
+
if (a.length !== b.length)
|
|
50
|
+
return false;
|
|
51
|
+
return a.every((line, i) => line.trim() === b[i].trim());
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Record a failure as a reflection entry.
|
|
55
|
+
*
|
|
56
|
+
* For objective sources this increments the self-repair count and reports
|
|
57
|
+
* whether progress was made — `progressed: false` means this attempt produced
|
|
58
|
+
* the exact same errors as the previous objective attempt (stagnation: the agent
|
|
59
|
+
* is stuck and further retries will only burn tokens).
|
|
60
|
+
*
|
|
61
|
+
* Subjective (review) entries are stored for completeness but never count toward
|
|
62
|
+
* the budget and always report progress (the reviewer channel handles them).
|
|
63
|
+
*/
|
|
64
|
+
export function recordReflection(state, entry) {
|
|
65
|
+
const trimmed = {
|
|
66
|
+
iteration: entry.iteration,
|
|
67
|
+
source: entry.source,
|
|
68
|
+
errors: entry.errors.map((e) => e.trim()).filter(Boolean).slice(0, MAX_ERRORS_PER_ENTRY),
|
|
69
|
+
};
|
|
70
|
+
if (!isObjective(trimmed.source)) {
|
|
71
|
+
state.entries.push(trimmed);
|
|
72
|
+
return { progressed: true };
|
|
73
|
+
}
|
|
74
|
+
const prev = lastObjectiveEntry(state);
|
|
75
|
+
const progressed = !prev || !sameErrors(prev.errors, trimmed.errors);
|
|
76
|
+
state.entries.push(trimmed);
|
|
77
|
+
state.reflectionCount += 1;
|
|
78
|
+
return { progressed };
|
|
79
|
+
}
|
|
80
|
+
/** True once the bounded self-repair budget is spent. */
|
|
81
|
+
export function shouldStopReflecting(state, max = DEFAULT_MAX_REFLECTIONS) {
|
|
82
|
+
return state.reflectionCount >= max;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Build the worker-prompt section that carries forward objective errors from
|
|
86
|
+
* prior attempts. Returns '' when there is nothing objective to reflect on, so
|
|
87
|
+
* the caller can simply concatenate.
|
|
88
|
+
*
|
|
89
|
+
* Only objective entries are emitted (subjective review feedback travels through
|
|
90
|
+
* the reviewer channel). The list is capped to the most recent MAX_TRAIL_ENTRIES.
|
|
91
|
+
*/
|
|
92
|
+
export function buildReflectionFeedback(state) {
|
|
93
|
+
const objective = state.entries.filter((e) => isObjective(e.source));
|
|
94
|
+
if (objective.length === 0)
|
|
95
|
+
return '';
|
|
96
|
+
const recent = objective.slice(-MAX_TRAIL_ENTRIES);
|
|
97
|
+
const lines = [];
|
|
98
|
+
lines.push('## Self-Repair Reflection (previous attempts failed these checks)');
|
|
99
|
+
lines.push('Your prior edits were rejected by automated checks below. Treat them as invalid edits: ' +
|
|
100
|
+
'fix the exact errors and do NOT repeat the same mistakes.');
|
|
101
|
+
for (const entry of recent) {
|
|
102
|
+
lines.push('');
|
|
103
|
+
lines.push(`### Attempt @ iteration ${entry.iteration} — ${labelForSource(entry.source)}`);
|
|
104
|
+
entry.errors.forEach((err, i) => lines.push(`${i + 1}. ${err}`));
|
|
105
|
+
}
|
|
106
|
+
return lines.join('\n');
|
|
107
|
+
}
|
|
108
|
+
function labelForSource(source) {
|
|
109
|
+
switch (source) {
|
|
110
|
+
case 'lint':
|
|
111
|
+
return 'lint / type check failed';
|
|
112
|
+
case 'bs':
|
|
113
|
+
return 'code-smell guard failed';
|
|
114
|
+
case 'test':
|
|
115
|
+
return 'tests failed';
|
|
116
|
+
case 'review':
|
|
117
|
+
return 'reviewer feedback';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=reflection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflection.js","sourceRoot":"","sources":["../../src/agents/reflection.ts"],"names":[],"mappings":"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"}
|
package/dist/agents/worker.d.ts
CHANGED
|
@@ -16,6 +16,14 @@ export interface WorkerOptions {
|
|
|
16
16
|
processContext?: ProcessContext;
|
|
17
17
|
/** 코드 컨텍스트 (impact analysis + registry briefs) */
|
|
18
18
|
workerContext?: WorkerContext;
|
|
19
|
+
/** no-edit 종료 가드 횟수 — 수정 필수 작업에서 모델이 edit 없이 끝내면 N회 되밂 */
|
|
20
|
+
nudgeMaxOnNoEdit?: number;
|
|
21
|
+
/** Verification-harness file protection — listed files reject edit/write */
|
|
22
|
+
protectedFiles?: string[];
|
|
23
|
+
/** bash tool timeout in ms — raise for slow verification such as docker-based tests */
|
|
24
|
+
bashTimeoutMs?: number;
|
|
25
|
+
/** Expose web_fetch + web_search tools (default true). Set false for SWE-bench integrity. */
|
|
26
|
+
webTools?: boolean;
|
|
19
27
|
}
|
|
20
28
|
/**
|
|
21
29
|
* Run Worker agent
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/agents/worker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAMxE,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,kDAAkD;IAClD,aAAa,CAAC,EAAE,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/agents/worker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAMxE,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,kDAAkD;IAClD,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAkBD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAsF7E;AAID;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CA0CT"}
|
package/dist/agents/worker.js
CHANGED
|
@@ -45,10 +45,21 @@ export async function runWorker(options) {
|
|
|
45
45
|
onLog: options.onLog,
|
|
46
46
|
processContext: options.processContext,
|
|
47
47
|
systemPrompt: getPrompts().systemPrompt,
|
|
48
|
+
// Worker is a mechanical execution role — file edits, not deep reasoning.
|
|
49
|
+
// Disable reasoning tokens to cut cost/latency (no-op on non-thinking models).
|
|
50
|
+
disableReasoning: true,
|
|
51
|
+
nudgeMaxOnNoEdit: options.nudgeMaxOnNoEdit,
|
|
52
|
+
protectedFiles: options.protectedFiles,
|
|
53
|
+
bashTimeoutMs: options.bashTimeoutMs,
|
|
54
|
+
webTools: options.webTools,
|
|
48
55
|
});
|
|
49
56
|
// Parse result via adapter
|
|
50
57
|
const parsedResult = adapter.parseWorkerOutput(raw);
|
|
51
|
-
//
|
|
58
|
+
// Git diff is the source of truth for "did real work happen" — independent of
|
|
59
|
+
// whether the model emitted a well-formed JSON success block. LLMs are weak at
|
|
60
|
+
// structured output, so we never let a missing/malformed JSON block alone mark
|
|
61
|
+
// a task as failed when the working tree actually changed (VEGA-style: real
|
|
62
|
+
// signal over self-report).
|
|
52
63
|
if (isGitRepo && snapshotHash) {
|
|
53
64
|
const gitChangedFiles = await gitTracker.getChangedFilesSinceSnapshot(cwd, snapshotHash);
|
|
54
65
|
if (gitChangedFiles.length > 0) {
|
|
@@ -59,6 +70,16 @@ export async function runWorker(options) {
|
|
|
59
70
|
...parsedResult.filesChanged,
|
|
60
71
|
]);
|
|
61
72
|
parsedResult.filesChanged = Array.from(mergedFiles);
|
|
73
|
+
// Real file changes + no explicit error signal → treat as success even if
|
|
74
|
+
// the model never produced a JSON block. Only an explicit error/halt in the
|
|
75
|
+
// output should keep success=false here.
|
|
76
|
+
if (!parsedResult.success && !parsedResult.error && !parsedResult.haltReason) {
|
|
77
|
+
console.log('[Worker] Promoting to success: git changes present, no error signal');
|
|
78
|
+
parsedResult.success = true;
|
|
79
|
+
if (!parsedResult.summary || parsedResult.summary === t('common.fallback.noSummary')) {
|
|
80
|
+
parsedResult.summary = `Modified ${gitChangedFiles.length} file(s): ${gitChangedFiles.slice(0, 5).join(', ')}`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
62
83
|
}
|
|
63
84
|
else if (parsedResult.filesChanged.length === 0) {
|
|
64
85
|
console.log('[Worker] No file changes detected by Git or LLM');
|
|
@@ -71,7 +92,7 @@ export async function runWorker(options) {
|
|
|
71
92
|
console.error(`[Worker] Execution failed: ${errMsg}`);
|
|
72
93
|
// Log stderr hint if available (CLI spawn errors often contain useful info)
|
|
73
94
|
if (error instanceof Error && error.message.includes('code')) {
|
|
74
|
-
console.error(`[Worker] CLI exited with non-zero code — check
|
|
95
|
+
console.error(`[Worker] CLI exited with non-zero code — check adapter auth and permissions`);
|
|
75
96
|
}
|
|
76
97
|
return {
|
|
77
98
|
success: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/agents/worker.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,2BAA2B;AAC3B,2CAA2C;AAC3C,+CAA+C;AAG/C,OAAO,KAAK,UAAU,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/agents/worker.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { AuthProfileStore, ensureValidToken, type AuthProfile } from './oauthStore.js';
|
|
2
|
-
export { runOAuthPkceFlow, loginAndSaveProfile, type OAuthFlowResult, type OAuthFlowOptions } from './oauthPkce.js';
|
|
2
|
+
export { runOAuthPkceFlow, loginAndSaveProfile, DEFAULT_OPENAI_CLIENT_ID, type OAuthFlowResult, type OAuthFlowOptions, } from './oauthPkce.js';
|
|
3
|
+
export { runOpenRouterPkceFlow, loginAndSaveOpenRouterProfile, saveOpenRouterApiKey, type OpenRouterFlowResult, type OpenRouterFlowOptions, } from './openrouterPkce.js';
|
|
4
|
+
export { runLinearPkceFlow, loginAndSaveLinearProfile, type LinearFlowResult, type LinearFlowOptions, } from './linearPkce.js';
|
|
3
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACvF,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACvF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,qBAAqB,EACrB,6BAA6B,EAC7B,oBAAoB,EACpB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC"}
|
package/dist/auth/index.js
CHANGED
|
@@ -2,5 +2,7 @@
|
|
|
2
2
|
// OpenSwarm - Auth Module
|
|
3
3
|
// ============================================
|
|
4
4
|
export { AuthProfileStore, ensureValidToken } from './oauthStore.js';
|
|
5
|
-
export { runOAuthPkceFlow, loginAndSaveProfile } from './oauthPkce.js';
|
|
5
|
+
export { runOAuthPkceFlow, loginAndSaveProfile, DEFAULT_OPENAI_CLIENT_ID, } from './oauthPkce.js';
|
|
6
|
+
export { runOpenRouterPkceFlow, loginAndSaveOpenRouterProfile, saveOpenRouterApiKey, } from './openrouterPkce.js';
|
|
7
|
+
export { runLinearPkceFlow, loginAndSaveLinearProfile, } from './linearPkce.js';
|
|
6
8
|
//# sourceMappingURL=index.js.map
|
package/dist/auth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,0BAA0B;AAC1B,+CAA+C;AAE/C,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAoB,MAAM,iBAAiB,CAAC;AACvF,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,0BAA0B;AAC1B,+CAA+C;AAE/C,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAoB,MAAM,iBAAiB,CAAC;AACvF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,wBAAwB,GAGzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,qBAAqB,EACrB,6BAA6B,EAC7B,oBAAoB,GAGrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iBAAiB,EACjB,yBAAyB,GAG1B,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const PROFILE_KEY = "linear:default";
|
|
2
|
+
export interface LinearFlowResult {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
refreshToken: string;
|
|
5
|
+
expiresIn: number;
|
|
6
|
+
}
|
|
7
|
+
export interface LinearFlowOptions {
|
|
8
|
+
port?: number;
|
|
9
|
+
scopes?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Linear OAuth 2.0 PKCE flow.
|
|
13
|
+
* 1. generate PKCE verifier/challenge + state
|
|
14
|
+
* 2. open https://linear.app/oauth/authorize?...&code_challenge=...&code_challenge_method=S256
|
|
15
|
+
* 3. receive ?code=... on the local callback server
|
|
16
|
+
* 4. POST https://api.linear.app/oauth/token (code_verifier, client_id — NO secret)
|
|
17
|
+
* → access_token + refresh_token
|
|
18
|
+
*/
|
|
19
|
+
export declare function runLinearPkceFlow(options?: LinearFlowOptions): Promise<LinearFlowResult>;
|
|
20
|
+
/** Full PKCE flow + persist the linear:default profile. Returns the access token. */
|
|
21
|
+
export declare function loginAndSaveLinearProfile(port?: number): Promise<string>;
|
|
22
|
+
//# sourceMappingURL=linearPkce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linearPkce.d.ts","sourceRoot":"","sources":["../../src/auth/linearPkce.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,WAAW,mBAAqB,CAAC;AA6C9C,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAkIlG;AAED,qFAAqF;AACrF,wBAAsB,yBAAyB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB9E"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// ============================================
|
|
2
|
+
// OpenSwarm - Linear OAuth 2.0 PKCE Flow
|
|
3
|
+
// Browser-based login that exchanges an authorization code for a Linear
|
|
4
|
+
// access_token (+ refresh_token). PKCE → NO client_secret is used or stored.
|
|
5
|
+
// ============================================
|
|
6
|
+
import { createServer } from 'node:http';
|
|
7
|
+
import { randomBytes, createHash } from 'node:crypto';
|
|
8
|
+
import { exec } from 'node:child_process';
|
|
9
|
+
import { AuthProfileStore } from './oauthStore.js';
|
|
10
|
+
// ----- Constants -----
|
|
11
|
+
const LINEAR_AUTH_ENDPOINT = 'https://linear.app/oauth/authorize';
|
|
12
|
+
const LINEAR_TOKEN_ENDPOINT = 'https://api.linear.app/oauth/token';
|
|
13
|
+
const DEFAULT_CALLBACK_PORT = 1457; // distinct from codex (1455) / openrouter (1456)
|
|
14
|
+
const DEFAULT_SCOPES = 'read,write';
|
|
15
|
+
const LOGIN_TIMEOUT_MS = 120_000;
|
|
16
|
+
const LINEAR_PROFILE_KEY = 'linear:default';
|
|
17
|
+
export const PROFILE_KEY = LINEAR_PROFILE_KEY;
|
|
18
|
+
/**
|
|
19
|
+
* Public OAuth client_id for the OpenSwarm Linear app. PKCE means no client
|
|
20
|
+
* secret is needed. Read from LINEAR_OAUTH_CLIENT_ID so the id is not baked into
|
|
21
|
+
* source/git; a future public build can set a hardcoded default here.
|
|
22
|
+
*/
|
|
23
|
+
function getClientId() {
|
|
24
|
+
const id = process.env.LINEAR_OAUTH_CLIENT_ID?.trim();
|
|
25
|
+
if (!id) {
|
|
26
|
+
// cxt-ignore-next-line: fake_data — real redirect URI shown in setup guidance
|
|
27
|
+
throw new Error('LINEAR_OAUTH_CLIENT_ID is not set. Register a Linear OAuth app at https://linear.app/settings/api/applications/new (redirect http://localhost:1457/callback, scopes read,write, PKCE) and set LINEAR_OAUTH_CLIENT_ID in .env.');
|
|
28
|
+
}
|
|
29
|
+
return id;
|
|
30
|
+
}
|
|
31
|
+
// ----- PKCE helpers (same shape as oauthPkce.ts / openrouterPkce.ts) -----
|
|
32
|
+
function generateCodeVerifier() {
|
|
33
|
+
return randomBytes(96).toString('base64url');
|
|
34
|
+
}
|
|
35
|
+
function generateCodeChallenge(verifier) {
|
|
36
|
+
return createHash('sha256').update(verifier).digest('base64url');
|
|
37
|
+
}
|
|
38
|
+
function generateState() {
|
|
39
|
+
return randomBytes(32).toString('hex');
|
|
40
|
+
}
|
|
41
|
+
// ----- Browser open (cross-platform) -----
|
|
42
|
+
function openBrowser(url) {
|
|
43
|
+
const platform = process.platform;
|
|
44
|
+
const cmd = platform === 'darwin' ? 'open' : platform === 'win32' ? 'start' : 'xdg-open';
|
|
45
|
+
exec(`${cmd} "${url}"`, (err) => {
|
|
46
|
+
if (err) {
|
|
47
|
+
console.error('[Auth] 브라우저를 자동으로 열 수 없습니다. 직접 열어주세요:');
|
|
48
|
+
console.error(url);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Linear OAuth 2.0 PKCE flow.
|
|
54
|
+
* 1. generate PKCE verifier/challenge + state
|
|
55
|
+
* 2. open https://linear.app/oauth/authorize?...&code_challenge=...&code_challenge_method=S256
|
|
56
|
+
* 3. receive ?code=... on the local callback server
|
|
57
|
+
* 4. POST https://api.linear.app/oauth/token (code_verifier, client_id — NO secret)
|
|
58
|
+
* → access_token + refresh_token
|
|
59
|
+
*/
|
|
60
|
+
export async function runLinearPkceFlow(options = {}) {
|
|
61
|
+
const { port = DEFAULT_CALLBACK_PORT, scopes = DEFAULT_SCOPES } = options;
|
|
62
|
+
const clientId = getClientId();
|
|
63
|
+
// Must match the redirect URI registered on the Linear OAuth app exactly.
|
|
64
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
65
|
+
const codeVerifier = generateCodeVerifier();
|
|
66
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
67
|
+
const state = generateState();
|
|
68
|
+
const authParams = new URLSearchParams({
|
|
69
|
+
response_type: 'code',
|
|
70
|
+
client_id: clientId,
|
|
71
|
+
redirect_uri: redirectUri,
|
|
72
|
+
code_challenge: codeChallenge,
|
|
73
|
+
code_challenge_method: 'S256',
|
|
74
|
+
scope: scopes,
|
|
75
|
+
state,
|
|
76
|
+
});
|
|
77
|
+
const authUrl = `${LINEAR_AUTH_ENDPOINT}?${authParams.toString()}`;
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
let settled = false;
|
|
80
|
+
const timeout = setTimeout(() => {
|
|
81
|
+
if (!settled) {
|
|
82
|
+
settled = true;
|
|
83
|
+
server.close();
|
|
84
|
+
reject(new Error('Linear login timed out (120s). 다시 시도하세요.'));
|
|
85
|
+
}
|
|
86
|
+
}, LOGIN_TIMEOUT_MS);
|
|
87
|
+
const server = createServer(async (req, res) => {
|
|
88
|
+
if (settled) {
|
|
89
|
+
res.writeHead(400);
|
|
90
|
+
res.end();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const url = new URL(req.url ?? '/', `http://127.0.0.1:${port}`);
|
|
94
|
+
if (url.pathname !== '/callback') {
|
|
95
|
+
res.writeHead(404);
|
|
96
|
+
res.end('Not found');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const code = url.searchParams.get('code');
|
|
100
|
+
const returnedState = url.searchParams.get('state');
|
|
101
|
+
const error = url.searchParams.get('error');
|
|
102
|
+
if (error) {
|
|
103
|
+
settled = true;
|
|
104
|
+
clearTimeout(timeout);
|
|
105
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
106
|
+
res.end(errorHtml(error));
|
|
107
|
+
server.close();
|
|
108
|
+
reject(new Error(`Linear OAuth error: ${error}`));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (!code || returnedState !== state) {
|
|
112
|
+
settled = true;
|
|
113
|
+
clearTimeout(timeout);
|
|
114
|
+
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
115
|
+
res.end(errorHtml('Invalid callback parameters'));
|
|
116
|
+
server.close();
|
|
117
|
+
reject(new Error('Invalid Linear callback: missing code or state mismatch'));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const tokenBody = new URLSearchParams({
|
|
122
|
+
grant_type: 'authorization_code',
|
|
123
|
+
code,
|
|
124
|
+
code_verifier: codeVerifier,
|
|
125
|
+
redirect_uri: redirectUri,
|
|
126
|
+
client_id: clientId,
|
|
127
|
+
});
|
|
128
|
+
const tokenRes = await fetch(LINEAR_TOKEN_ENDPOINT, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
131
|
+
body: tokenBody.toString(),
|
|
132
|
+
});
|
|
133
|
+
if (!tokenRes.ok) {
|
|
134
|
+
const errText = await tokenRes.text().catch(() => '');
|
|
135
|
+
throw new Error(`Token exchange failed (${tokenRes.status}): ${errText.slice(0, 300)}`);
|
|
136
|
+
}
|
|
137
|
+
const tokens = (await tokenRes.json());
|
|
138
|
+
settled = true;
|
|
139
|
+
clearTimeout(timeout);
|
|
140
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
141
|
+
res.end(successHtml());
|
|
142
|
+
server.close();
|
|
143
|
+
resolve({
|
|
144
|
+
accessToken: tokens.access_token,
|
|
145
|
+
refreshToken: tokens.refresh_token ?? '',
|
|
146
|
+
expiresIn: tokens.expires_in,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
settled = true;
|
|
151
|
+
clearTimeout(timeout);
|
|
152
|
+
res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
153
|
+
res.end(errorHtml(String(err)));
|
|
154
|
+
server.close();
|
|
155
|
+
reject(err);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
server.listen(port, '127.0.0.1', () => {
|
|
159
|
+
console.log(`[Auth] Callback server listening on http://127.0.0.1:${port}`);
|
|
160
|
+
console.log('[Auth] 브라우저에서 Linear 로그인 페이지를 엽니다...');
|
|
161
|
+
openBrowser(authUrl);
|
|
162
|
+
});
|
|
163
|
+
server.on('error', (err) => {
|
|
164
|
+
if (!settled) {
|
|
165
|
+
settled = true;
|
|
166
|
+
clearTimeout(timeout);
|
|
167
|
+
reject(new Error(`Callback server error: ${err.message}`));
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/** Full PKCE flow + persist the linear:default profile. Returns the access token. */
|
|
173
|
+
export async function loginAndSaveLinearProfile(port) {
|
|
174
|
+
const result = await runLinearPkceFlow({ port });
|
|
175
|
+
const profile = {
|
|
176
|
+
type: 'oauth',
|
|
177
|
+
provider: 'linear',
|
|
178
|
+
access: result.accessToken,
|
|
179
|
+
refresh: result.refreshToken,
|
|
180
|
+
expires: Date.now() + result.expiresIn * 1000,
|
|
181
|
+
clientId: getClientId(),
|
|
182
|
+
};
|
|
183
|
+
const store = new AuthProfileStore();
|
|
184
|
+
store.setProfile(LINEAR_PROFILE_KEY, profile);
|
|
185
|
+
console.log(`[Auth] Linear OAuth 인증 완료. 프로필 저장됨: ${LINEAR_PROFILE_KEY}`); // cxt-ignore: fake_execution — printed after a real token exchange
|
|
186
|
+
return result.accessToken;
|
|
187
|
+
}
|
|
188
|
+
// ----- HTML templates -----
|
|
189
|
+
function successHtml() {
|
|
190
|
+
return `<!DOCTYPE html>
|
|
191
|
+
<html><head><meta charset="utf-8"><title>OpenSwarm Auth</title>
|
|
192
|
+
<style>body{font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#f0fdf4}
|
|
193
|
+
.card{text-align:center;padding:2rem;border-radius:12px;background:white;box-shadow:0 2px 8px rgba(0,0,0,0.1)}
|
|
194
|
+
h1{color:#16a34a;margin-bottom:0.5rem}p{color:#666}</style></head>
|
|
195
|
+
<body><div class="card"><h1>✓ 인증 완료</h1><p>OpenSwarm에 Linear 인증이 완료되었습니다.<br>이 창을 닫아도 됩니다.</p></div></body></html>`;
|
|
196
|
+
}
|
|
197
|
+
function errorHtml(error) {
|
|
198
|
+
return `<!DOCTYPE html>
|
|
199
|
+
<html><head><meta charset="utf-8"><title>OpenSwarm Auth Error</title>
|
|
200
|
+
<style>body{font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#fef2f2}
|
|
201
|
+
.card{text-align:center;padding:2rem;border-radius:12px;background:white;box-shadow:0 2px 8px rgba(0,0,0,0.1)}
|
|
202
|
+
h1{color:#dc2626;margin-bottom:0.5rem}p{color:#666}</style></head>
|
|
203
|
+
<body><div class="card"><h1>✗ 인증 실패</h1><p>${escapeHtml(error)}</p><p>터미널에서 다시 시도하세요.</p></div></body></html>`;
|
|
204
|
+
}
|
|
205
|
+
function escapeHtml(s) {
|
|
206
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=linearPkce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linearPkce.js","sourceRoot":"","sources":["../../src/auth/linearPkce.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/oauthPkce.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const DEFAULT_OPENAI_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
1
2
|
export interface OAuthFlowResult {
|
|
2
3
|
accessToken: string;
|
|
3
4
|
refreshToken: string;
|
|
@@ -5,7 +6,7 @@ export interface OAuthFlowResult {
|
|
|
5
6
|
accountId?: string;
|
|
6
7
|
}
|
|
7
8
|
export interface OAuthFlowOptions {
|
|
8
|
-
clientId
|
|
9
|
+
clientId?: string;
|
|
9
10
|
port?: number;
|
|
10
11
|
scopes?: string;
|
|
11
12
|
}
|
|
@@ -13,9 +14,9 @@ export interface OAuthFlowOptions {
|
|
|
13
14
|
* OAuth 2.1 PKCE 흐름 실행.
|
|
14
15
|
* 로컬 HTTP 서버에서 callback을 받고, token을 교환하여 저장한다.
|
|
15
16
|
*/
|
|
16
|
-
export declare function runOAuthPkceFlow(options
|
|
17
|
+
export declare function runOAuthPkceFlow(options?: OAuthFlowOptions): Promise<OAuthFlowResult>;
|
|
17
18
|
/**
|
|
18
19
|
* OAuth 로그인 → 토큰 저장 (온보딩 전체 흐름)
|
|
19
20
|
*/
|
|
20
|
-
export declare function loginAndSaveProfile(clientId
|
|
21
|
+
export declare function loginAndSaveProfile(clientId?: string, port?: number): Promise<void>;
|
|
21
22
|
//# sourceMappingURL=oauthPkce.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauthPkce.d.ts","sourceRoot":"","sources":["../../src/auth/oauthPkce.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oauthPkce.d.ts","sourceRoot":"","sources":["../../src/auth/oauthPkce.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,wBAAwB,iCAAiC,CAAC;AAqCvE,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA+K/F;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,GAAE,MAAiC,EAC3C,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAoBf"}
|
package/dist/auth/oauthPkce.js
CHANGED
|
@@ -10,9 +10,15 @@ import { AuthProfileStore } from './oauthStore.js';
|
|
|
10
10
|
const OPENAI_AUTH_ENDPOINT = 'https://auth.openai.com/oauth/authorize';
|
|
11
11
|
const OPENAI_TOKEN_ENDPOINT = 'https://auth.openai.com/oauth/token';
|
|
12
12
|
const DEFAULT_CALLBACK_PORT = 1455;
|
|
13
|
-
const DEFAULT_SCOPES = 'openid profile email offline_access
|
|
14
|
-
const LOGIN_TIMEOUT_MS = 120_000; // 2
|
|
13
|
+
const DEFAULT_SCOPES = 'openid profile email offline_access';
|
|
14
|
+
const LOGIN_TIMEOUT_MS = 120_000; // 2 minutes
|
|
15
15
|
const PROFILE_KEY = 'openai-gpt:default';
|
|
16
|
+
// Public OAuth client_id used by the official @openai/codex CLI.
|
|
17
|
+
// Reusing it lets `openswarm auth login --provider gpt` work out of the box for
|
|
18
|
+
// any ChatGPT Plus/Pro/Team user without provisioning a custom OAuth app.
|
|
19
|
+
// Override with `--client-id` or the OPENAI_CLIENT_ID env var if needed.
|
|
20
|
+
export const DEFAULT_OPENAI_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
|
|
21
|
+
const OAUTH_ORIGINATOR = 'openswarm';
|
|
16
22
|
// PKCE helpers
|
|
17
23
|
function generateCodeVerifier() {
|
|
18
24
|
// 43-128자 base64url-safe 랜덤 문자열
|
|
@@ -41,14 +47,19 @@ function openBrowser(url) {
|
|
|
41
47
|
* OAuth 2.1 PKCE 흐름 실행.
|
|
42
48
|
* 로컬 HTTP 서버에서 callback을 받고, token을 교환하여 저장한다.
|
|
43
49
|
*/
|
|
44
|
-
export async function runOAuthPkceFlow(options) {
|
|
45
|
-
const { clientId, port = DEFAULT_CALLBACK_PORT, scopes = DEFAULT_SCOPES } = options;
|
|
46
|
-
|
|
50
|
+
export async function runOAuthPkceFlow(options = {}) {
|
|
51
|
+
const { clientId = DEFAULT_OPENAI_CLIENT_ID, port = DEFAULT_CALLBACK_PORT, scopes = DEFAULT_SCOPES, } = options;
|
|
52
|
+
// Must be exactly "http://localhost:1455/auth/callback" — this is the value
|
|
53
|
+
// registered on the public Codex OAuth client. Using 127.0.0.1 instead
|
|
54
|
+
// triggers Hydra's authorize_hydra_invalid_request error.
|
|
55
|
+
const redirectUri = `http://localhost:${port}/auth/callback`;
|
|
47
56
|
// 1. PKCE 생성
|
|
48
57
|
const codeVerifier = generateCodeVerifier();
|
|
49
58
|
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
50
59
|
const state = generateState();
|
|
51
|
-
// 2. Authorization URL
|
|
60
|
+
// 2. Authorization URL 구성.
|
|
61
|
+
// The simplified_flow + id_token_add_organizations params mirror the official
|
|
62
|
+
// codex CLI so the ChatGPT side recognises this as a first-party desktop login.
|
|
52
63
|
const authParams = new URLSearchParams({
|
|
53
64
|
response_type: 'code',
|
|
54
65
|
client_id: clientId,
|
|
@@ -57,6 +68,9 @@ export async function runOAuthPkceFlow(options) {
|
|
|
57
68
|
code_challenge_method: 'S256',
|
|
58
69
|
scope: scopes,
|
|
59
70
|
state,
|
|
71
|
+
id_token_add_organizations: 'true',
|
|
72
|
+
codex_cli_simplified_flow: 'true',
|
|
73
|
+
originator: OAUTH_ORIGINATOR,
|
|
60
74
|
});
|
|
61
75
|
const authUrl = `${OPENAI_AUTH_ENDPOINT}?${authParams.toString()}`;
|
|
62
76
|
// 3. 로컬 HTTP 서버 시작 + callback 대기
|
|
@@ -121,15 +135,28 @@ export async function runOAuthPkceFlow(options) {
|
|
|
121
135
|
throw new Error(`Token exchange failed (${tokenRes.status}): ${errText.slice(0, 300)}`);
|
|
122
136
|
}
|
|
123
137
|
const tokens = (await tokenRes.json());
|
|
124
|
-
//
|
|
138
|
+
// Codex 백엔드(/responses, /models)는 `chatgpt-account-id` 헤더를 요구한다.
|
|
139
|
+
// 그 값은 access_token JWT의 `https://api.openai.com/auth` claim 안의
|
|
140
|
+
// `chatgpt_account_id`다. (과거엔 id_token.sub를 저장했는데, 그건 IdP
|
|
141
|
+
// subject — 예: `google-oauth2|...` — 라서 codex account_id가 아니다.)
|
|
142
|
+
// access_token 우선, 없으면 id_token으로 폴백.
|
|
125
143
|
let accountId;
|
|
126
|
-
|
|
144
|
+
for (const jwt of [tokens.access_token, tokens.id_token]) {
|
|
145
|
+
if (!jwt)
|
|
146
|
+
continue;
|
|
127
147
|
try {
|
|
128
|
-
const payload = JSON.parse(Buffer.from(
|
|
129
|
-
|
|
148
|
+
const payload = JSON.parse(Buffer.from(jwt.split('.')[1], 'base64url').toString());
|
|
149
|
+
const authClaim = payload['https://api.openai.com/auth'];
|
|
150
|
+
const candidate = authClaim && typeof authClaim === 'object'
|
|
151
|
+
? authClaim.chatgpt_account_id
|
|
152
|
+
: undefined;
|
|
153
|
+
if (typeof candidate === 'string' && candidate) {
|
|
154
|
+
accountId = candidate;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
130
157
|
}
|
|
131
158
|
catch {
|
|
132
|
-
//
|
|
159
|
+
// JWT 파싱 실패는 무시 — accountId 없이 진행
|
|
133
160
|
}
|
|
134
161
|
}
|
|
135
162
|
const result = {
|
|
@@ -171,7 +198,7 @@ export async function runOAuthPkceFlow(options) {
|
|
|
171
198
|
/**
|
|
172
199
|
* OAuth 로그인 → 토큰 저장 (온보딩 전체 흐름)
|
|
173
200
|
*/
|
|
174
|
-
export async function loginAndSaveProfile(clientId, port) {
|
|
201
|
+
export async function loginAndSaveProfile(clientId = DEFAULT_OPENAI_CLIENT_ID, port) {
|
|
175
202
|
const result = await runOAuthPkceFlow({ clientId, port });
|
|
176
203
|
const profile = {
|
|
177
204
|
type: 'oauth',
|