@locusai/sdk 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/worker.d.ts.map +1 -1
- package/dist/agent/worker.js +14 -1
- package/dist/ai/claude-runner.d.ts.map +1 -1
- package/dist/ai/claude-runner.js +7 -2
- package/dist/events.d.ts +2 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/index-node.d.ts +1 -0
- package/dist/index-node.d.ts.map +1 -1
- package/dist/index-node.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +12 -11
- package/dist/utils/colors.d.ts +48 -0
- package/dist/utils/colors.d.ts.map +1 -0
- package/dist/utils/colors.js +54 -0
- package/dist/utils/retry.d.ts +13 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +37 -0
- package/package.json +2 -2
- package/src/agent/worker.ts +15 -1
- package/src/ai/claude-runner.ts +7 -2
- package/src/events.ts +3 -0
- package/src/index-node.ts +3 -0
- package/src/index.ts +41 -1
- package/src/orchestrator.ts +14 -11
- package/src/utils/colors.ts +63 -0
- package/src/utils/retry.ts +56 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/agent/worker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/agent/worker.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,qBAAa,WAAW;IAmBV,OAAO,CAAC,MAAM;IAlB1B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,eAAe,CAAyB;IAGhD,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IAGnC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,UAAU,CAAuB;gBAErB,MAAM,EAAE,YAAY;IAqExC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAgB;YAe5D,eAAe;YAcf,WAAW;YAiBX,WAAW;IAqBnB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA+F3B"}
|
package/dist/agent/worker.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AnthropicClient } from "../ai/anthropic-client";
|
|
2
2
|
import { ClaudeRunner } from "../ai/claude-runner";
|
|
3
3
|
import { LocusClient } from "../index";
|
|
4
|
+
import { c } from "../utils/colors";
|
|
4
5
|
import { ArtifactSyncer } from "./artifact-syncer";
|
|
5
6
|
import { CodebaseIndexerService } from "./codebase-indexer-service";
|
|
6
7
|
import { SprintPlanner } from "./sprint-planner";
|
|
@@ -33,6 +34,12 @@ export class AgentWorker {
|
|
|
33
34
|
this.client = new LocusClient({
|
|
34
35
|
baseUrl: config.apiBase,
|
|
35
36
|
token: config.apiKey,
|
|
37
|
+
retryOptions: {
|
|
38
|
+
maxRetries: 3,
|
|
39
|
+
initialDelay: 1000,
|
|
40
|
+
maxDelay: 5000,
|
|
41
|
+
factor: 2,
|
|
42
|
+
},
|
|
36
43
|
});
|
|
37
44
|
// Initialize AI clients
|
|
38
45
|
this.claudeRunner = new ClaudeRunner(projectPath, config.model);
|
|
@@ -78,8 +85,14 @@ export class AgentWorker {
|
|
|
78
85
|
}
|
|
79
86
|
log(message, level = "info") {
|
|
80
87
|
const timestamp = new Date().toISOString().split("T")[1]?.slice(0, 8) ?? "";
|
|
88
|
+
const colorFn = {
|
|
89
|
+
info: c.cyan,
|
|
90
|
+
success: c.green,
|
|
91
|
+
warn: c.yellow,
|
|
92
|
+
error: c.red,
|
|
93
|
+
}[level];
|
|
81
94
|
const prefix = { info: "ℹ", success: "✓", warn: "⚠", error: "✗" }[level];
|
|
82
|
-
console.log(`[${timestamp}] [${this.config.agentId.slice(-8)}] ${prefix} ${message}`);
|
|
95
|
+
console.log(`${c.dim(`[${timestamp}]`)} ${c.bold(`[${this.config.agentId.slice(-8)}]`)} ${colorFn(`${prefix} ${message}`)}`);
|
|
83
96
|
}
|
|
84
97
|
async getActiveSprint() {
|
|
85
98
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAGA,qBAAa,YAAY;IAErB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;gBADL,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAsB;IAGvC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../../src/ai/claude-runner.ts"],"names":[],"mappings":"AAGA,qBAAa,YAAY;IAErB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,KAAK;gBADL,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAsB;IAGvC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;CA4C1D"}
|
package/dist/ai/claude-runner.js
CHANGED
|
@@ -33,8 +33,13 @@ export class ClaudeRunner {
|
|
|
33
33
|
claude.on("close", (code) => {
|
|
34
34
|
if (code === 0)
|
|
35
35
|
resolve(output);
|
|
36
|
-
else
|
|
37
|
-
|
|
36
|
+
else {
|
|
37
|
+
const detail = errorOutput.trim();
|
|
38
|
+
const message = detail
|
|
39
|
+
? `Claude CLI error: ${detail}`
|
|
40
|
+
: `Claude CLI exited with code ${code}. Please ensure the Claude CLI is installed and you are logged in (run 'claude' manually to check).`;
|
|
41
|
+
reject(new Error(message));
|
|
42
|
+
}
|
|
38
43
|
});
|
|
39
44
|
claude.stdin.write(prompt);
|
|
40
45
|
claude.stdin.end();
|
package/dist/events.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
|
+
import { RetryOptions } from "./utils/retry";
|
|
2
3
|
export declare enum LocusEvent {
|
|
3
4
|
TOKEN_EXPIRED = "TOKEN_EXPIRED",
|
|
4
5
|
AUTH_ERROR = "AUTH_ERROR",
|
|
@@ -8,6 +9,7 @@ export interface LocusConfig {
|
|
|
8
9
|
baseUrl: string;
|
|
9
10
|
token?: string | null;
|
|
10
11
|
timeout?: number;
|
|
12
|
+
retryOptions?: RetryOptions;
|
|
11
13
|
}
|
|
12
14
|
export declare class LocusEmitter extends EventEmitter {
|
|
13
15
|
on(event: LocusEvent.TOKEN_EXPIRED, listener: () => void): this;
|
package/dist/events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,oBAAY,UAAU;IACpB,aAAa,kBAAkB;IAC/B,UAAU,eAAe;IACzB,aAAa,kBAAkB;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,oBAAY,UAAU;IACpB,aAAa,kBAAkB;IAC/B,UAAU,eAAe;IACzB,aAAa,kBAAkB;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,qBAAa,YAAa,SAAQ,YAAY;IAC5C,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAC/D,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IACxE,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAQ3E,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,GAAG,OAAO;IAC9C,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO;IACzD,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,aAAa,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO;CAI7D"}
|
package/dist/index-node.d.ts
CHANGED
package/dist/index-node.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-node.d.ts","sourceRoot":"","sources":["../src/index-node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,cAAc,SAAS,CAAC;AAExB,cAAc,MAAM,CAAC;AAErB,cAAc,QAAQ,CAAC;AAEvB,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index-node.d.ts","sourceRoot":"","sources":["../src/index-node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,cAAc,SAAS,CAAC;AAExB,cAAc,MAAM,CAAC;AAErB,cAAc,QAAQ,CAAC;AAEvB,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAG5E,OAAO,EAAE,CAAC,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index-node.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAc,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAc,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAIxD,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AAErC,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IACpC,SAAgB,OAAO,EAAE,YAAY,CAAC;IAEtC,SAAgB,IAAI,EAAE,UAAU,CAAC;IACjC,SAAgB,KAAK,EAAE,WAAW,CAAC;IACnC,SAAgB,OAAO,EAAE,aAAa,CAAC;IACvC,SAAgB,UAAU,EAAE,gBAAgB,CAAC;IAC7C,SAAgB,aAAa,EAAE,mBAAmB,CAAC;IACnD,SAAgB,WAAW,EAAE,iBAAiB,CAAC;IAC/C,SAAgB,IAAI,EAAE,UAAU,CAAC;IACjC,SAAgB,EAAE,EAAE,QAAQ,CAAC;gBAEjB,MAAM,EAAE,WAAW;IA6B/B,OAAO,CAAC,qBAAqB;IAmC7B,OAAO,CAAC,iBAAiB;IAmDlB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAOrC"}
|
package/dist/index.js
CHANGED
|
@@ -49,6 +49,31 @@ export class LocusClient {
|
|
|
49
49
|
this.invitations = new InvitationsModule(this.api, this.emitter);
|
|
50
50
|
this.docs = new DocsModule(this.api, this.emitter);
|
|
51
51
|
this.ci = new CiModule(this.api, this.emitter);
|
|
52
|
+
if (config.retryOptions) {
|
|
53
|
+
this.setupRetryInterceptor(config.retryOptions);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
setupRetryInterceptor(retryOptions) {
|
|
57
|
+
this.api.interceptors.response.use(undefined, async (error) => {
|
|
58
|
+
const config = error.config;
|
|
59
|
+
if (!config || !retryOptions) {
|
|
60
|
+
return Promise.reject(error);
|
|
61
|
+
}
|
|
62
|
+
config._retryCount = config._retryCount || 0;
|
|
63
|
+
const maxRetries = retryOptions.maxRetries ?? 3;
|
|
64
|
+
const shouldRetry = config._retryCount < maxRetries &&
|
|
65
|
+
(retryOptions.retryCondition
|
|
66
|
+
? retryOptions.retryCondition(error)
|
|
67
|
+
: !error.response || error.response.status >= 500);
|
|
68
|
+
if (shouldRetry) {
|
|
69
|
+
config._retryCount++;
|
|
70
|
+
const delay = Math.min((retryOptions.initialDelay ?? 1000) *
|
|
71
|
+
Math.pow(retryOptions.factor ?? 2, config._retryCount - 1), retryOptions.maxDelay ?? 5000);
|
|
72
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
73
|
+
return this.api(config);
|
|
74
|
+
}
|
|
75
|
+
return Promise.reject(error);
|
|
76
|
+
});
|
|
52
77
|
}
|
|
53
78
|
setupInterceptors() {
|
|
54
79
|
this.api.interceptors.response.use((response) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAGzD,OAAO,EAAE,IAAI,EAA4B,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAGzD,OAAO,EAAE,IAAI,EAA4B,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IACpD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,gBAAgB,CAAuB;gBAEnC,MAAM,EAAE,kBAAkB;IAStC;;OAEG;YACW,eAAe;IAwB7B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB5B;;OAEG;YACW,iBAAiB;IA4C/B;;OAEG;IACH,OAAO,CAAC,eAAe;IAYvB;;OAEG;YACW,UAAU;IA8HxB;;OAEG;YACW,UAAU;IAKxB;;OAEG;YACW,iBAAiB;IAc/B;;OAEG;IACG,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IA2C9D;;OAEG;IACG,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IA4BhB;;OAEG;IACG,QAAQ,CACZ,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC;IAyBhB;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAM3B;;OAEG;YACW,OAAO;IAWrB;;OAEG;IACH,QAAQ;;;;;;IAeR,OAAO,CAAC,KAAK;CAGd"}
|
package/dist/orchestrator.js
CHANGED
|
@@ -4,6 +4,7 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
import { TaskPriority, TaskStatus } from "@locusai/shared";
|
|
5
5
|
import { EventEmitter } from "events";
|
|
6
6
|
import { LocusClient } from "./index";
|
|
7
|
+
import { c } from "./utils/colors";
|
|
7
8
|
export class AgentOrchestrator extends EventEmitter {
|
|
8
9
|
client;
|
|
9
10
|
config;
|
|
@@ -30,14 +31,14 @@ export class AgentOrchestrator extends EventEmitter {
|
|
|
30
31
|
try {
|
|
31
32
|
const sprint = await this.client.sprints.getActive(this.config.workspaceId);
|
|
32
33
|
if (sprint?.id) {
|
|
33
|
-
console.log(`📋 Using active sprint: ${sprint.name}`);
|
|
34
|
+
console.log(c.info(`📋 Using active sprint: ${sprint.name}`));
|
|
34
35
|
return sprint.id;
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
catch {
|
|
38
39
|
// No active sprint found, will work with all tasks
|
|
39
40
|
}
|
|
40
|
-
console.log("ℹ No sprint specified, working with all workspace tasks");
|
|
41
|
+
console.log(c.dim("ℹ No sprint specified, working with all workspace tasks"));
|
|
41
42
|
return "";
|
|
42
43
|
}
|
|
43
44
|
/**
|
|
@@ -71,18 +72,18 @@ export class AgentOrchestrator extends EventEmitter {
|
|
|
71
72
|
config: this.config,
|
|
72
73
|
sprintId: this.resolvedSprintId,
|
|
73
74
|
});
|
|
74
|
-
console.log("
|
|
75
|
-
console.log("
|
|
76
|
-
console.log(
|
|
75
|
+
console.log(`\n${c.primary("🤖 Locus Agent Orchestrator")}`);
|
|
76
|
+
console.log(c.dim("----------------------------------------------"));
|
|
77
|
+
console.log(`${c.bold("Workspace:")} ${this.config.workspaceId}`);
|
|
77
78
|
if (this.resolvedSprintId) {
|
|
78
|
-
console.log(
|
|
79
|
+
console.log(`${c.bold("Sprint:")} ${this.resolvedSprintId}`);
|
|
79
80
|
}
|
|
80
|
-
console.log(
|
|
81
|
-
console.log("
|
|
81
|
+
console.log(`${c.bold("API Base:")} ${this.config.apiBase}`);
|
|
82
|
+
console.log(c.dim("----------------------------------------------\n"));
|
|
82
83
|
// Check if there are tasks to work on before spawning
|
|
83
84
|
const tasks = await this.getAvailableTasks();
|
|
84
85
|
if (tasks.length === 0) {
|
|
85
|
-
console.log("ℹ No available tasks found in the backlog.");
|
|
86
|
+
console.log(c.dim("ℹ No available tasks found in the backlog."));
|
|
86
87
|
return;
|
|
87
88
|
}
|
|
88
89
|
// Spawn single agent
|
|
@@ -95,7 +96,7 @@ export class AgentOrchestrator extends EventEmitter {
|
|
|
95
96
|
}
|
|
96
97
|
await this.sleep(2000);
|
|
97
98
|
}
|
|
98
|
-
console.log("
|
|
99
|
+
console.log(`\n${c.success("✅ Orchestrator finished")}`);
|
|
99
100
|
}
|
|
100
101
|
/**
|
|
101
102
|
* Find the package root by looking for package.json
|
|
@@ -127,7 +128,7 @@ export class AgentOrchestrator extends EventEmitter {
|
|
|
127
128
|
lastHeartbeat: new Date(),
|
|
128
129
|
};
|
|
129
130
|
this.agents.set(agentId, agentState);
|
|
130
|
-
console.log(
|
|
131
|
+
console.log(`${c.primary("🚀 Agent started:")} ${c.bold(agentId)}\n`);
|
|
131
132
|
// Build arguments for agent worker
|
|
132
133
|
// Try multiple resolution strategies
|
|
133
134
|
const potentialPaths = [];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple ANSI color utility for terminal output
|
|
3
|
+
* Dependency-free and works in Node.js/Bun environments
|
|
4
|
+
*/
|
|
5
|
+
declare const colors: {
|
|
6
|
+
reset: string;
|
|
7
|
+
bold: string;
|
|
8
|
+
dim: string;
|
|
9
|
+
italic: string;
|
|
10
|
+
underline: string;
|
|
11
|
+
black: string;
|
|
12
|
+
red: string;
|
|
13
|
+
green: string;
|
|
14
|
+
yellow: string;
|
|
15
|
+
blue: string;
|
|
16
|
+
magenta: string;
|
|
17
|
+
cyan: string;
|
|
18
|
+
white: string;
|
|
19
|
+
gray: string;
|
|
20
|
+
brightRed: string;
|
|
21
|
+
brightGreen: string;
|
|
22
|
+
brightYellow: string;
|
|
23
|
+
brightBlue: string;
|
|
24
|
+
brightMagenta: string;
|
|
25
|
+
brightCyan: string;
|
|
26
|
+
brightWhite: string;
|
|
27
|
+
};
|
|
28
|
+
type ColorName = keyof typeof colors;
|
|
29
|
+
export declare const c: {
|
|
30
|
+
text: (text: string, ...colorNames: ColorName[]) => string;
|
|
31
|
+
bold: (t: string) => string;
|
|
32
|
+
dim: (t: string) => string;
|
|
33
|
+
red: (t: string) => string;
|
|
34
|
+
green: (t: string) => string;
|
|
35
|
+
yellow: (t: string) => string;
|
|
36
|
+
blue: (t: string) => string;
|
|
37
|
+
magenta: (t: string) => string;
|
|
38
|
+
cyan: (t: string) => string;
|
|
39
|
+
gray: (t: string) => string;
|
|
40
|
+
success: (t: string) => string;
|
|
41
|
+
error: (t: string) => string;
|
|
42
|
+
warning: (t: string) => string;
|
|
43
|
+
info: (t: string) => string;
|
|
44
|
+
primary: (t: string) => string;
|
|
45
|
+
underline: (t: string) => string;
|
|
46
|
+
};
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=colors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../src/utils/colors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;CA0BX,CAAC;AAEF,KAAK,SAAS,GAAG,MAAM,OAAO,MAAM,CAAC;AAErC,eAAO,MAAM,CAAC;iBACC,MAAM,iBAAiB,SAAS,EAAE;cAMrC,MAAM;aACP,MAAM;aACN,MAAM;eACJ,MAAM;gBACL,MAAM;cACR,MAAM;iBACH,MAAM;cACT,MAAM;cACN,MAAM;iBAGH,MAAM;eACR,MAAM;iBACJ,MAAM;cACT,MAAM;iBACH,MAAM;mBACJ,MAAM;CACtB,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple ANSI color utility for terminal output
|
|
3
|
+
* Dependency-free and works in Node.js/Bun environments
|
|
4
|
+
*/
|
|
5
|
+
const ESC = "\u001b[";
|
|
6
|
+
const RESET = `${ESC}0m`;
|
|
7
|
+
const colors = {
|
|
8
|
+
reset: RESET,
|
|
9
|
+
bold: `${ESC}1m`,
|
|
10
|
+
dim: `${ESC}2m`,
|
|
11
|
+
italic: `${ESC}3m`,
|
|
12
|
+
underline: `${ESC}4m`,
|
|
13
|
+
// Foreground colors
|
|
14
|
+
black: `${ESC}30m`,
|
|
15
|
+
red: `${ESC}31m`,
|
|
16
|
+
green: `${ESC}32m`,
|
|
17
|
+
yellow: `${ESC}33m`,
|
|
18
|
+
blue: `${ESC}34m`,
|
|
19
|
+
magenta: `${ESC}35m`,
|
|
20
|
+
cyan: `${ESC}36m`,
|
|
21
|
+
white: `${ESC}37m`,
|
|
22
|
+
gray: `${ESC}90m`,
|
|
23
|
+
// Foreground bright colors
|
|
24
|
+
brightRed: `${ESC}91m`,
|
|
25
|
+
brightGreen: `${ESC}92m`,
|
|
26
|
+
brightYellow: `${ESC}93m`,
|
|
27
|
+
brightBlue: `${ESC}94m`,
|
|
28
|
+
brightMagenta: `${ESC}95m`,
|
|
29
|
+
brightCyan: `${ESC}96m`,
|
|
30
|
+
brightWhite: `${ESC}97m`,
|
|
31
|
+
};
|
|
32
|
+
export const c = {
|
|
33
|
+
text: (text, ...colorNames) => {
|
|
34
|
+
const codes = colorNames.map((name) => colors[name]).join("");
|
|
35
|
+
return `${codes}${text}${RESET}`;
|
|
36
|
+
},
|
|
37
|
+
// Shortcuts
|
|
38
|
+
bold: (t) => c.text(t, "bold"),
|
|
39
|
+
dim: (t) => c.text(t, "dim"),
|
|
40
|
+
red: (t) => c.text(t, "red"),
|
|
41
|
+
green: (t) => c.text(t, "green"),
|
|
42
|
+
yellow: (t) => c.text(t, "yellow"),
|
|
43
|
+
blue: (t) => c.text(t, "blue"),
|
|
44
|
+
magenta: (t) => c.text(t, "magenta"),
|
|
45
|
+
cyan: (t) => c.text(t, "cyan"),
|
|
46
|
+
gray: (t) => c.text(t, "gray"),
|
|
47
|
+
// Combinations
|
|
48
|
+
success: (t) => c.text(t, "green", "bold"),
|
|
49
|
+
error: (t) => c.text(t, "red", "bold"),
|
|
50
|
+
warning: (t) => c.text(t, "yellow", "bold"),
|
|
51
|
+
info: (t) => c.text(t, "cyan", "bold"),
|
|
52
|
+
primary: (t) => c.text(t, "blue", "bold"),
|
|
53
|
+
underline: (t) => c.text(t, "underline"),
|
|
54
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface RetryOptions {
|
|
2
|
+
maxRetries?: number;
|
|
3
|
+
initialDelay?: number;
|
|
4
|
+
maxDelay?: number;
|
|
5
|
+
factor?: number;
|
|
6
|
+
retryCondition?: (error: unknown) => boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare const DEFAULT_RETRY_OPTIONS: Required<RetryOptions>;
|
|
9
|
+
/**
|
|
10
|
+
* Retries an async function with exponential backoff
|
|
11
|
+
*/
|
|
12
|
+
export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
13
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/utils/retry.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;CAC9C;AAED,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,YAAY,CAaxD,CAAC;AAEF;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,CAAC,CAAC,CAwBZ"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { isAxiosError } from "axios";
|
|
2
|
+
export const DEFAULT_RETRY_OPTIONS = {
|
|
3
|
+
maxRetries: 3,
|
|
4
|
+
initialDelay: 1000,
|
|
5
|
+
maxDelay: 5000,
|
|
6
|
+
factor: 2,
|
|
7
|
+
retryCondition: (error) => {
|
|
8
|
+
// Retry on network errors or 5xx server errors
|
|
9
|
+
if (isAxiosError(error)) {
|
|
10
|
+
if (!error.response)
|
|
11
|
+
return true; // Network error
|
|
12
|
+
return error.response.status >= 500;
|
|
13
|
+
}
|
|
14
|
+
return true; // Retry on other unknown errors
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Retries an async function with exponential backoff
|
|
19
|
+
*/
|
|
20
|
+
export async function withRetry(fn, options = {}) {
|
|
21
|
+
const config = { ...DEFAULT_RETRY_OPTIONS, ...options };
|
|
22
|
+
let lastError;
|
|
23
|
+
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
24
|
+
try {
|
|
25
|
+
return await fn();
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
lastError = error;
|
|
29
|
+
if (attempt === config.maxRetries || !config.retryCondition(error)) {
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
const delay = Math.min(config.initialDelay * Math.pow(config.factor, attempt), config.maxDelay);
|
|
33
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
throw lastError;
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusai/sdk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@anthropic-ai/sdk": "^0.71.2",
|
|
44
|
-
"@locusai/shared": "^0.4.
|
|
44
|
+
"@locusai/shared": "^0.4.1",
|
|
45
45
|
"axios": "^1.13.2",
|
|
46
46
|
"events": "^3.3.0",
|
|
47
47
|
"globby": "^14.0.2"
|
package/src/agent/worker.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Sprint, Task, TaskStatus } from "@locusai/shared";
|
|
|
2
2
|
import { AnthropicClient } from "../ai/anthropic-client";
|
|
3
3
|
import { ClaudeRunner } from "../ai/claude-runner";
|
|
4
4
|
import { LocusClient } from "../index";
|
|
5
|
+
import { c } from "../utils/colors";
|
|
5
6
|
import { ArtifactSyncer } from "./artifact-syncer";
|
|
6
7
|
import { CodebaseIndexerService } from "./codebase-indexer-service";
|
|
7
8
|
import { SprintPlanner } from "./sprint-planner";
|
|
@@ -48,6 +49,12 @@ export class AgentWorker {
|
|
|
48
49
|
this.client = new LocusClient({
|
|
49
50
|
baseUrl: config.apiBase,
|
|
50
51
|
token: config.apiKey,
|
|
52
|
+
retryOptions: {
|
|
53
|
+
maxRetries: 3,
|
|
54
|
+
initialDelay: 1000,
|
|
55
|
+
maxDelay: 5000,
|
|
56
|
+
factor: 2,
|
|
57
|
+
},
|
|
51
58
|
});
|
|
52
59
|
|
|
53
60
|
// Initialize AI clients
|
|
@@ -106,9 +113,16 @@ export class AgentWorker {
|
|
|
106
113
|
|
|
107
114
|
log(message: string, level: "info" | "success" | "warn" | "error" = "info") {
|
|
108
115
|
const timestamp = new Date().toISOString().split("T")[1]?.slice(0, 8) ?? "";
|
|
116
|
+
const colorFn = {
|
|
117
|
+
info: c.cyan,
|
|
118
|
+
success: c.green,
|
|
119
|
+
warn: c.yellow,
|
|
120
|
+
error: c.red,
|
|
121
|
+
}[level];
|
|
109
122
|
const prefix = { info: "ℹ", success: "✓", warn: "⚠", error: "✗" }[level];
|
|
123
|
+
|
|
110
124
|
console.log(
|
|
111
|
-
`[${timestamp}] [${this.config.agentId.slice(-8)}] ${prefix} ${message}`
|
|
125
|
+
`${c.dim(`[${timestamp}]`)} ${c.bold(`[${this.config.agentId.slice(-8)}]`)} ${colorFn(`${prefix} ${message}`)}`
|
|
112
126
|
);
|
|
113
127
|
}
|
|
114
128
|
|
package/src/ai/claude-runner.ts
CHANGED
|
@@ -38,8 +38,13 @@ export class ClaudeRunner {
|
|
|
38
38
|
);
|
|
39
39
|
claude.on("close", (code) => {
|
|
40
40
|
if (code === 0) resolve(output);
|
|
41
|
-
else
|
|
42
|
-
|
|
41
|
+
else {
|
|
42
|
+
const detail = errorOutput.trim();
|
|
43
|
+
const message = detail
|
|
44
|
+
? `Claude CLI error: ${detail}`
|
|
45
|
+
: `Claude CLI exited with code ${code}. Please ensure the Claude CLI is installed and you are logged in (run 'claude' manually to check).`;
|
|
46
|
+
reject(new Error(message));
|
|
47
|
+
}
|
|
43
48
|
});
|
|
44
49
|
|
|
45
50
|
claude.stdin.write(prompt);
|
package/src/events.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
2
|
|
|
3
|
+
import { RetryOptions } from "./utils/retry";
|
|
4
|
+
|
|
3
5
|
export enum LocusEvent {
|
|
4
6
|
TOKEN_EXPIRED = "TOKEN_EXPIRED",
|
|
5
7
|
AUTH_ERROR = "AUTH_ERROR",
|
|
@@ -10,6 +12,7 @@ export interface LocusConfig {
|
|
|
10
12
|
baseUrl: string;
|
|
11
13
|
token?: string | null;
|
|
12
14
|
timeout?: number;
|
|
15
|
+
retryOptions?: RetryOptions;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
export class LocusEmitter extends EventEmitter {
|
package/src/index-node.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import axios, { AxiosInstance } from "axios";
|
|
1
|
+
import axios, { AxiosInstance, InternalAxiosRequestConfig } from "axios";
|
|
2
2
|
import { LocusConfig, LocusEmitter, LocusEvent } from "./events";
|
|
3
3
|
import { AuthModule } from "./modules/auth";
|
|
4
4
|
import { CiModule } from "./modules/ci";
|
|
@@ -8,6 +8,7 @@ import { OrganizationsModule } from "./modules/organizations";
|
|
|
8
8
|
import { SprintsModule } from "./modules/sprints";
|
|
9
9
|
import { TasksModule } from "./modules/tasks";
|
|
10
10
|
import { WorkspacesModule } from "./modules/workspaces";
|
|
11
|
+
import { RetryOptions } from "./utils/retry";
|
|
11
12
|
|
|
12
13
|
// Browser-safe exports only
|
|
13
14
|
export * from "./events";
|
|
@@ -56,6 +57,45 @@ export class LocusClient {
|
|
|
56
57
|
this.invitations = new InvitationsModule(this.api, this.emitter);
|
|
57
58
|
this.docs = new DocsModule(this.api, this.emitter);
|
|
58
59
|
this.ci = new CiModule(this.api, this.emitter);
|
|
60
|
+
|
|
61
|
+
if (config.retryOptions) {
|
|
62
|
+
this.setupRetryInterceptor(config.retryOptions);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private setupRetryInterceptor(retryOptions: RetryOptions) {
|
|
67
|
+
this.api.interceptors.response.use(undefined, async (error) => {
|
|
68
|
+
const config = error.config as InternalAxiosRequestConfig & {
|
|
69
|
+
_retryCount?: number;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (!config || !retryOptions) {
|
|
73
|
+
return Promise.reject(error);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
config._retryCount = config._retryCount || 0;
|
|
77
|
+
|
|
78
|
+
const maxRetries = retryOptions.maxRetries ?? 3;
|
|
79
|
+
const shouldRetry =
|
|
80
|
+
config._retryCount < maxRetries &&
|
|
81
|
+
(retryOptions.retryCondition
|
|
82
|
+
? retryOptions.retryCondition(error)
|
|
83
|
+
: !error.response || error.response.status >= 500);
|
|
84
|
+
|
|
85
|
+
if (shouldRetry) {
|
|
86
|
+
config._retryCount++;
|
|
87
|
+
const delay = Math.min(
|
|
88
|
+
(retryOptions.initialDelay ?? 1000) *
|
|
89
|
+
Math.pow(retryOptions.factor ?? 2, config._retryCount - 1),
|
|
90
|
+
retryOptions.maxDelay ?? 5000
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
94
|
+
return this.api(config);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return Promise.reject(error);
|
|
98
|
+
});
|
|
59
99
|
}
|
|
60
100
|
|
|
61
101
|
private setupInterceptors() {
|
package/src/orchestrator.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
import { Task, TaskPriority, TaskStatus } from "@locusai/shared";
|
|
5
5
|
import { EventEmitter } from "events";
|
|
6
6
|
import { LocusClient } from "./index";
|
|
7
|
+
import { c } from "./utils/colors";
|
|
7
8
|
|
|
8
9
|
export interface AgentConfig {
|
|
9
10
|
id: string;
|
|
@@ -62,14 +63,16 @@ export class AgentOrchestrator extends EventEmitter {
|
|
|
62
63
|
this.config.workspaceId
|
|
63
64
|
);
|
|
64
65
|
if (sprint?.id) {
|
|
65
|
-
console.log(`📋 Using active sprint: ${sprint.name}`);
|
|
66
|
+
console.log(c.info(`📋 Using active sprint: ${sprint.name}`));
|
|
66
67
|
return sprint.id;
|
|
67
68
|
}
|
|
68
69
|
} catch {
|
|
69
70
|
// No active sprint found, will work with all tasks
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
console.log(
|
|
73
|
+
console.log(
|
|
74
|
+
c.dim("ℹ No sprint specified, working with all workspace tasks")
|
|
75
|
+
);
|
|
73
76
|
return "";
|
|
74
77
|
}
|
|
75
78
|
|
|
@@ -107,20 +110,20 @@ export class AgentOrchestrator extends EventEmitter {
|
|
|
107
110
|
sprintId: this.resolvedSprintId,
|
|
108
111
|
});
|
|
109
112
|
|
|
110
|
-
console.log("
|
|
111
|
-
console.log("
|
|
112
|
-
console.log(
|
|
113
|
+
console.log(`\n${c.primary("🤖 Locus Agent Orchestrator")}`);
|
|
114
|
+
console.log(c.dim("----------------------------------------------"));
|
|
115
|
+
console.log(`${c.bold("Workspace:")} ${this.config.workspaceId}`);
|
|
113
116
|
if (this.resolvedSprintId) {
|
|
114
|
-
console.log(
|
|
117
|
+
console.log(`${c.bold("Sprint:")} ${this.resolvedSprintId}`);
|
|
115
118
|
}
|
|
116
|
-
console.log(
|
|
117
|
-
console.log("
|
|
119
|
+
console.log(`${c.bold("API Base:")} ${this.config.apiBase}`);
|
|
120
|
+
console.log(c.dim("----------------------------------------------\n"));
|
|
118
121
|
|
|
119
122
|
// Check if there are tasks to work on before spawning
|
|
120
123
|
const tasks = await this.getAvailableTasks();
|
|
121
124
|
|
|
122
125
|
if (tasks.length === 0) {
|
|
123
|
-
console.log("ℹ No available tasks found in the backlog.");
|
|
126
|
+
console.log(c.dim("ℹ No available tasks found in the backlog."));
|
|
124
127
|
return;
|
|
125
128
|
}
|
|
126
129
|
|
|
@@ -138,7 +141,7 @@ export class AgentOrchestrator extends EventEmitter {
|
|
|
138
141
|
await this.sleep(2000);
|
|
139
142
|
}
|
|
140
143
|
|
|
141
|
-
console.log("
|
|
144
|
+
console.log(`\n${c.success("✅ Orchestrator finished")}`);
|
|
142
145
|
}
|
|
143
146
|
|
|
144
147
|
/**
|
|
@@ -175,7 +178,7 @@ export class AgentOrchestrator extends EventEmitter {
|
|
|
175
178
|
|
|
176
179
|
this.agents.set(agentId, agentState);
|
|
177
180
|
|
|
178
|
-
console.log(
|
|
181
|
+
console.log(`${c.primary("🚀 Agent started:")} ${c.bold(agentId)}\n`);
|
|
179
182
|
|
|
180
183
|
// Build arguments for agent worker
|
|
181
184
|
// Try multiple resolution strategies
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple ANSI color utility for terminal output
|
|
3
|
+
* Dependency-free and works in Node.js/Bun environments
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const ESC = "\u001b[";
|
|
7
|
+
const RESET = `${ESC}0m`;
|
|
8
|
+
|
|
9
|
+
const colors = {
|
|
10
|
+
reset: RESET,
|
|
11
|
+
bold: `${ESC}1m`,
|
|
12
|
+
dim: `${ESC}2m`,
|
|
13
|
+
italic: `${ESC}3m`,
|
|
14
|
+
underline: `${ESC}4m`,
|
|
15
|
+
|
|
16
|
+
// Foreground colors
|
|
17
|
+
black: `${ESC}30m`,
|
|
18
|
+
red: `${ESC}31m`,
|
|
19
|
+
green: `${ESC}32m`,
|
|
20
|
+
yellow: `${ESC}33m`,
|
|
21
|
+
blue: `${ESC}34m`,
|
|
22
|
+
magenta: `${ESC}35m`,
|
|
23
|
+
cyan: `${ESC}36m`,
|
|
24
|
+
white: `${ESC}37m`,
|
|
25
|
+
gray: `${ESC}90m`,
|
|
26
|
+
|
|
27
|
+
// Foreground bright colors
|
|
28
|
+
brightRed: `${ESC}91m`,
|
|
29
|
+
brightGreen: `${ESC}92m`,
|
|
30
|
+
brightYellow: `${ESC}93m`,
|
|
31
|
+
brightBlue: `${ESC}94m`,
|
|
32
|
+
brightMagenta: `${ESC}95m`,
|
|
33
|
+
brightCyan: `${ESC}96m`,
|
|
34
|
+
brightWhite: `${ESC}97m`,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type ColorName = keyof typeof colors;
|
|
38
|
+
|
|
39
|
+
export const c = {
|
|
40
|
+
text: (text: string, ...colorNames: ColorName[]) => {
|
|
41
|
+
const codes = colorNames.map((name) => colors[name]).join("");
|
|
42
|
+
return `${codes}${text}${RESET}`;
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Shortcuts
|
|
46
|
+
bold: (t: string) => c.text(t, "bold"),
|
|
47
|
+
dim: (t: string) => c.text(t, "dim"),
|
|
48
|
+
red: (t: string) => c.text(t, "red"),
|
|
49
|
+
green: (t: string) => c.text(t, "green"),
|
|
50
|
+
yellow: (t: string) => c.text(t, "yellow"),
|
|
51
|
+
blue: (t: string) => c.text(t, "blue"),
|
|
52
|
+
magenta: (t: string) => c.text(t, "magenta"),
|
|
53
|
+
cyan: (t: string) => c.text(t, "cyan"),
|
|
54
|
+
gray: (t: string) => c.text(t, "gray"),
|
|
55
|
+
|
|
56
|
+
// Combinations
|
|
57
|
+
success: (t: string) => c.text(t, "green", "bold"),
|
|
58
|
+
error: (t: string) => c.text(t, "red", "bold"),
|
|
59
|
+
warning: (t: string) => c.text(t, "yellow", "bold"),
|
|
60
|
+
info: (t: string) => c.text(t, "cyan", "bold"),
|
|
61
|
+
primary: (t: string) => c.text(t, "blue", "bold"),
|
|
62
|
+
underline: (t: string) => c.text(t, "underline"),
|
|
63
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { isAxiosError } from "axios";
|
|
2
|
+
|
|
3
|
+
export interface RetryOptions {
|
|
4
|
+
maxRetries?: number;
|
|
5
|
+
initialDelay?: number;
|
|
6
|
+
maxDelay?: number;
|
|
7
|
+
factor?: number;
|
|
8
|
+
retryCondition?: (error: unknown) => boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_RETRY_OPTIONS: Required<RetryOptions> = {
|
|
12
|
+
maxRetries: 3,
|
|
13
|
+
initialDelay: 1000,
|
|
14
|
+
maxDelay: 5000,
|
|
15
|
+
factor: 2,
|
|
16
|
+
retryCondition: (error: unknown) => {
|
|
17
|
+
// Retry on network errors or 5xx server errors
|
|
18
|
+
if (isAxiosError(error)) {
|
|
19
|
+
if (!error.response) return true; // Network error
|
|
20
|
+
return error.response.status >= 500;
|
|
21
|
+
}
|
|
22
|
+
return true; // Retry on other unknown errors
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Retries an async function with exponential backoff
|
|
28
|
+
*/
|
|
29
|
+
export async function withRetry<T>(
|
|
30
|
+
fn: () => Promise<T>,
|
|
31
|
+
options: RetryOptions = {}
|
|
32
|
+
): Promise<T> {
|
|
33
|
+
const config = { ...DEFAULT_RETRY_OPTIONS, ...options };
|
|
34
|
+
let lastError: unknown;
|
|
35
|
+
|
|
36
|
+
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
37
|
+
try {
|
|
38
|
+
return await fn();
|
|
39
|
+
} catch (error: unknown) {
|
|
40
|
+
lastError = error;
|
|
41
|
+
|
|
42
|
+
if (attempt === config.maxRetries || !config.retryCondition(error)) {
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const delay = Math.min(
|
|
47
|
+
config.initialDelay * Math.pow(config.factor, attempt),
|
|
48
|
+
config.maxDelay
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
throw lastError;
|
|
56
|
+
}
|