@locusai/sdk 0.4.6 → 0.4.9
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/index-node.js +1590 -20
- package/dist/index.js +429 -121
- package/dist/orchestrator.d.ts.map +1 -1
- package/package.json +12 -23
- package/dist/agent/artifact-syncer.js +0 -77
- package/dist/agent/codebase-indexer-service.js +0 -55
- package/dist/agent/index.js +0 -5
- package/dist/agent/sprint-planner.js +0 -68
- package/dist/agent/task-executor.js +0 -60
- package/dist/agent/worker.js +0 -252
- package/dist/ai/anthropic-client.js +0 -70
- package/dist/ai/claude-runner.js +0 -71
- package/dist/ai/index.js +0 -2
- package/dist/core/config.js +0 -15
- package/dist/core/index.js +0 -3
- package/dist/core/indexer.js +0 -113
- package/dist/core/prompt-builder.js +0 -83
- package/dist/events.js +0 -15
- package/dist/modules/auth.js +0 -23
- package/dist/modules/base.js +0 -8
- package/dist/modules/ci.js +0 -7
- package/dist/modules/docs.js +0 -38
- package/dist/modules/invitations.js +0 -22
- package/dist/modules/organizations.js +0 -39
- package/dist/modules/sprints.js +0 -34
- package/dist/modules/tasks.js +0 -56
- package/dist/modules/workspaces.js +0 -49
- package/dist/orchestrator.js +0 -356
- package/dist/utils/colors.js +0 -54
- package/dist/utils/retry.js +0 -37
- package/src/agent/artifact-syncer.ts +0 -111
- package/src/agent/codebase-indexer-service.ts +0 -71
- package/src/agent/index.ts +0 -5
- package/src/agent/sprint-planner.ts +0 -86
- package/src/agent/task-executor.ts +0 -85
- package/src/agent/worker.ts +0 -322
- package/src/ai/anthropic-client.ts +0 -93
- package/src/ai/claude-runner.ts +0 -86
- package/src/ai/index.ts +0 -2
- package/src/core/config.ts +0 -21
- package/src/core/index.ts +0 -3
- package/src/core/indexer.ts +0 -131
- package/src/core/prompt-builder.ts +0 -91
- package/src/events.ts +0 -35
- package/src/index-node.ts +0 -23
- package/src/index.ts +0 -159
- package/src/modules/auth.ts +0 -48
- package/src/modules/base.ts +0 -9
- package/src/modules/ci.ts +0 -12
- package/src/modules/docs.ts +0 -84
- package/src/modules/invitations.ts +0 -45
- package/src/modules/organizations.ts +0 -90
- package/src/modules/sprints.ts +0 -69
- package/src/modules/tasks.ts +0 -110
- package/src/modules/workspaces.ts +0 -94
- package/src/orchestrator.ts +0 -473
- package/src/utils/colors.ts +0 -63
- package/src/utils/retry.ts +0 -56
package/src/core/indexer.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { globby } from "globby";
|
|
4
|
-
|
|
5
|
-
export interface CodebaseIndex {
|
|
6
|
-
symbols: Record<string, string[]>; // symbol -> file paths
|
|
7
|
-
responsibilities: Record<string, string>; // file path -> brief summary
|
|
8
|
-
lastIndexed: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class CodebaseIndexer {
|
|
12
|
-
private projectPath: string;
|
|
13
|
-
private indexPath: string;
|
|
14
|
-
|
|
15
|
-
constructor(projectPath: string) {
|
|
16
|
-
this.projectPath = projectPath;
|
|
17
|
-
this.indexPath = join(projectPath, ".locus", "codebase-index.json");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Generates a codebase index by providing the entire file tree to an AI summarizer.
|
|
22
|
-
* This is much more efficient than per-file indexing for large projects.
|
|
23
|
-
*/
|
|
24
|
-
async index(
|
|
25
|
-
onProgress?: (message: string) => void,
|
|
26
|
-
treeSummarizer?: (tree: string) => Promise<CodebaseIndex>
|
|
27
|
-
): Promise<CodebaseIndex> {
|
|
28
|
-
if (!treeSummarizer) {
|
|
29
|
-
throw new Error("A treeSummarizer is required for this indexing method.");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (onProgress) onProgress("Generating file tree...");
|
|
33
|
-
|
|
34
|
-
// 1. Get a comprehensive but clean file tree
|
|
35
|
-
const gitmodulesPath = join(this.projectPath, ".gitmodules");
|
|
36
|
-
const submoduleIgnores: string[] = [];
|
|
37
|
-
if (existsSync(gitmodulesPath)) {
|
|
38
|
-
try {
|
|
39
|
-
const content = readFileSync(gitmodulesPath, "utf-8");
|
|
40
|
-
const lines = content.split("\n");
|
|
41
|
-
for (const line of lines) {
|
|
42
|
-
const match = line.match(/^\s*path\s*=\s*(.*)$/);
|
|
43
|
-
const path = match?.[1]?.trim();
|
|
44
|
-
if (path) {
|
|
45
|
-
submoduleIgnores.push(`${path}/**`);
|
|
46
|
-
submoduleIgnores.push(`**/${path}/**`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
} catch {
|
|
50
|
-
// Fallback if .gitmodules exists but can't be read or parsed
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const files = await globby(["**/*"], {
|
|
55
|
-
cwd: this.projectPath,
|
|
56
|
-
gitignore: true,
|
|
57
|
-
ignore: [
|
|
58
|
-
...submoduleIgnores,
|
|
59
|
-
"**/node_modules/**",
|
|
60
|
-
"**/dist/**",
|
|
61
|
-
"**/build/**",
|
|
62
|
-
"**/target/**", // Rust build artifacts
|
|
63
|
-
"**/bin/**",
|
|
64
|
-
"**/obj/**",
|
|
65
|
-
"**/.next/**",
|
|
66
|
-
"**/.svelte-kit/**",
|
|
67
|
-
"**/.nuxt/**",
|
|
68
|
-
"**/.cache/**",
|
|
69
|
-
"**/out/**",
|
|
70
|
-
"**/__tests__/**",
|
|
71
|
-
"**/coverage/**",
|
|
72
|
-
"**/*.test.*",
|
|
73
|
-
"**/*.spec.*",
|
|
74
|
-
"**/*.d.ts",
|
|
75
|
-
"**/tsconfig.tsbuildinfo",
|
|
76
|
-
"**/.locus/*.json", // Ignore index and other system JSONs
|
|
77
|
-
"**/.locus/*.md", // Ignore system MDs
|
|
78
|
-
"**/.locus/!(artifacts)/**", // Ignore everything in .locus EXCEPT artifacts
|
|
79
|
-
"**/.git/**",
|
|
80
|
-
"**/.svn/**",
|
|
81
|
-
"**/.hg/**",
|
|
82
|
-
"**/.vscode/**",
|
|
83
|
-
"**/.idea/**",
|
|
84
|
-
"**/.DS_Store",
|
|
85
|
-
"**/bun.lock",
|
|
86
|
-
"**/package-lock.json",
|
|
87
|
-
"**/yarn.lock",
|
|
88
|
-
"**/pnpm-lock.yaml",
|
|
89
|
-
"**/Cargo.lock",
|
|
90
|
-
"**/go.sum",
|
|
91
|
-
"**/poetry.lock",
|
|
92
|
-
// Binary/Large Assets
|
|
93
|
-
"**/*.{png,jpg,jpeg,gif,svg,ico,mp4,webm,wav,mp3,woff,woff2,eot,ttf,otf,pdf,zip,tar.gz,rar}",
|
|
94
|
-
],
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Format the tree for the AI
|
|
98
|
-
const treeString = files.join("\n");
|
|
99
|
-
|
|
100
|
-
if (onProgress) onProgress("AI is analyzing codebase structure...");
|
|
101
|
-
|
|
102
|
-
// 2. Ask AI to generate the index based on the tree
|
|
103
|
-
const index = await treeSummarizer(treeString);
|
|
104
|
-
|
|
105
|
-
// 3. Post-process: Ensure symbols are extracted for core files if not provided by AI
|
|
106
|
-
// (AI is good at structure, but might miss specific exports unless it reads the files)
|
|
107
|
-
// For now, we trust the AI's structural summary and can supplement symbols later if needed.
|
|
108
|
-
|
|
109
|
-
index.lastIndexed = new Date().toISOString();
|
|
110
|
-
return index;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
loadIndex(): CodebaseIndex | null {
|
|
114
|
-
if (existsSync(this.indexPath)) {
|
|
115
|
-
try {
|
|
116
|
-
return JSON.parse(readFileSync(this.indexPath, "utf-8"));
|
|
117
|
-
} catch {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
saveIndex(index: CodebaseIndex): void {
|
|
125
|
-
const dir = dirname(this.indexPath);
|
|
126
|
-
if (!existsSync(dir)) {
|
|
127
|
-
mkdirSync(dir, { recursive: true });
|
|
128
|
-
}
|
|
129
|
-
writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { AssigneeRole, Task } from "@locusai/shared";
|
|
3
|
-
import { getLocusPath } from "./config.js";
|
|
4
|
-
|
|
5
|
-
export class PromptBuilder {
|
|
6
|
-
constructor(private projectPath: string) {}
|
|
7
|
-
|
|
8
|
-
async build(task: Task): Promise<string> {
|
|
9
|
-
let prompt = `# Task: ${task.title}\n\n`;
|
|
10
|
-
|
|
11
|
-
const roleText = this.roleToText(task.assigneeRole);
|
|
12
|
-
if (roleText) {
|
|
13
|
-
prompt += `## Role\nYou are acting as a ${roleText}.\n\n`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
prompt += `## Description\n${task.description || "No description provided."}\n\n`;
|
|
17
|
-
|
|
18
|
-
// 1. Add CLAUDE.md context
|
|
19
|
-
const contextPath = getLocusPath(this.projectPath, "contextFile");
|
|
20
|
-
if (existsSync(contextPath)) {
|
|
21
|
-
try {
|
|
22
|
-
const context = readFileSync(contextPath, "utf-8");
|
|
23
|
-
prompt += `## Project Context (from CLAUDE.md)\n${context}\n\n`;
|
|
24
|
-
} catch (err) {
|
|
25
|
-
console.warn(`Warning: Could not read context file: ${err}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// 2. Add Codebase Index context
|
|
30
|
-
const indexPath = getLocusPath(this.projectPath, "indexFile");
|
|
31
|
-
if (existsSync(indexPath)) {
|
|
32
|
-
prompt += `## Codebase Overview\nThere is an index file in the .locus/codebase-index.json and if you need you can check it.\n\n`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 3. Add Documents
|
|
36
|
-
if (task.docs && task.docs.length > 0) {
|
|
37
|
-
prompt += `## Attached Documents\n`;
|
|
38
|
-
for (const doc of task.docs) {
|
|
39
|
-
prompt += `### ${doc.title}\n${doc.content || "(No content)"}\n\n`;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// 4. Add Checklist
|
|
44
|
-
if (task.acceptanceChecklist && task.acceptanceChecklist.length > 0) {
|
|
45
|
-
prompt += `## Acceptance Criteria\n`;
|
|
46
|
-
for (const item of task.acceptanceChecklist) {
|
|
47
|
-
prompt += `- ${item.done ? "[x]" : "[ ]"} ${item.text}\n`;
|
|
48
|
-
}
|
|
49
|
-
prompt += "\n";
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// 5. Add Comments & Feedback
|
|
53
|
-
if (task.comments && task.comments.length > 0) {
|
|
54
|
-
const comments = task.comments.slice(0, 5);
|
|
55
|
-
prompt += `## Task History & Feedback\n`;
|
|
56
|
-
prompt += `Review the following comments for context or rejection feedback:\n\n`;
|
|
57
|
-
for (const comment of comments) {
|
|
58
|
-
const date = new Date(comment.createdAt).toLocaleString();
|
|
59
|
-
prompt += `### ${comment.author} (${date})\n${comment.text}\n\n`;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
prompt += `## Instructions
|
|
64
|
-
1. Complete this task.
|
|
65
|
-
2. **Artifact Management**: If you create any high-level documentation (PRDs, technical drafts, architecture docs), you MUST save them in \`.locus/artifacts/\`. Do NOT create them in the root directory.
|
|
66
|
-
3. **Paths**: Use relative paths from the project root at all times. Do NOT use absolute local paths (e.g., /Users/...).
|
|
67
|
-
4. When finished successfully, output: <promise>COMPLETE</promise>\n`;
|
|
68
|
-
return prompt;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
roleToText(role: Task["assigneeRole"]): string | null {
|
|
72
|
-
if (!role) {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
switch (role) {
|
|
77
|
-
case AssigneeRole.BACKEND:
|
|
78
|
-
return "Backend Engineer";
|
|
79
|
-
case AssigneeRole.FRONTEND:
|
|
80
|
-
return "Frontend Engineer";
|
|
81
|
-
case AssigneeRole.PM:
|
|
82
|
-
return "Product Manager";
|
|
83
|
-
case AssigneeRole.QA:
|
|
84
|
-
return "QA Engineer";
|
|
85
|
-
case AssigneeRole.DESIGN:
|
|
86
|
-
return "Product Designer";
|
|
87
|
-
default:
|
|
88
|
-
return "engineer";
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
package/src/events.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "events";
|
|
2
|
-
|
|
3
|
-
import { RetryOptions } from "./utils/retry.js";
|
|
4
|
-
|
|
5
|
-
export enum LocusEvent {
|
|
6
|
-
TOKEN_EXPIRED = "TOKEN_EXPIRED",
|
|
7
|
-
AUTH_ERROR = "AUTH_ERROR",
|
|
8
|
-
REQUEST_ERROR = "REQUEST_ERROR",
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface LocusConfig {
|
|
12
|
-
baseUrl: string;
|
|
13
|
-
token?: string | null;
|
|
14
|
-
timeout?: number;
|
|
15
|
-
retryOptions?: RetryOptions;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class LocusEmitter extends EventEmitter {
|
|
19
|
-
on(event: LocusEvent.TOKEN_EXPIRED, listener: () => void): this;
|
|
20
|
-
on(event: LocusEvent.AUTH_ERROR, listener: (error: Error) => void): this;
|
|
21
|
-
on(event: LocusEvent.REQUEST_ERROR, listener: (error: Error) => void): this;
|
|
22
|
-
on(
|
|
23
|
-
event: LocusEvent | string,
|
|
24
|
-
listener: ((...args: unknown[]) => void) | (() => void)
|
|
25
|
-
): this {
|
|
26
|
-
return super.on(event, listener);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
emit(event: LocusEvent.TOKEN_EXPIRED): boolean;
|
|
30
|
-
emit(event: LocusEvent.AUTH_ERROR, error: Error): boolean;
|
|
31
|
-
emit(event: LocusEvent.REQUEST_ERROR, error: Error): boolean;
|
|
32
|
-
emit(event: LocusEvent | string, ...args: unknown[]): boolean {
|
|
33
|
-
return super.emit(event, ...args);
|
|
34
|
-
}
|
|
35
|
-
}
|
package/src/index-node.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Node.js-only exports
|
|
3
|
-
* This is a separate entry point for Node.js/CLI only
|
|
4
|
-
* It should NOT be imported by browser applications
|
|
5
|
-
*
|
|
6
|
-
* These modules use Node.js APIs (fs, child_process, etc.)
|
|
7
|
-
* and will break in browser environments
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
// Node.js-only: Agent system
|
|
11
|
-
export * from "./agent/index.js";
|
|
12
|
-
// Node.js-only: AI clients
|
|
13
|
-
export * from "./ai/index.js";
|
|
14
|
-
// Node.js-only: Core utilities (uses fs)
|
|
15
|
-
export * from "./core/index.js";
|
|
16
|
-
// Re-export everything from main index (browser-safe)
|
|
17
|
-
export * from "./index.js";
|
|
18
|
-
|
|
19
|
-
// Node.js-only: Orchestrator
|
|
20
|
-
export { AgentOrchestrator, type OrchestratorConfig } from "./orchestrator.js";
|
|
21
|
-
|
|
22
|
-
// Utilities
|
|
23
|
-
export { c } from "./utils/colors.js";
|
package/src/index.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import axios, { AxiosInstance, InternalAxiosRequestConfig } from "axios";
|
|
2
|
-
import { LocusConfig, LocusEmitter, LocusEvent } from "./events.js";
|
|
3
|
-
import { AuthModule } from "./modules/auth.js";
|
|
4
|
-
import { CiModule } from "./modules/ci.js";
|
|
5
|
-
import { DocsModule } from "./modules/docs.js";
|
|
6
|
-
import { InvitationsModule } from "./modules/invitations.js";
|
|
7
|
-
import { OrganizationsModule } from "./modules/organizations.js";
|
|
8
|
-
import { SprintsModule } from "./modules/sprints.js";
|
|
9
|
-
import { TasksModule } from "./modules/tasks.js";
|
|
10
|
-
import { WorkspacesModule } from "./modules/workspaces.js";
|
|
11
|
-
import { RetryOptions } from "./utils/retry.js";
|
|
12
|
-
|
|
13
|
-
// Browser-safe exports only
|
|
14
|
-
export * from "./events.js";
|
|
15
|
-
export * from "./modules/auth.js";
|
|
16
|
-
export * from "./modules/ci.js";
|
|
17
|
-
export * from "./modules/docs.js";
|
|
18
|
-
export * from "./modules/invitations.js";
|
|
19
|
-
export * from "./modules/organizations.js";
|
|
20
|
-
export * from "./modules/sprints.js";
|
|
21
|
-
export * from "./modules/tasks.js";
|
|
22
|
-
export * from "./modules/workspaces.js";
|
|
23
|
-
|
|
24
|
-
export class LocusClient {
|
|
25
|
-
private readonly api: AxiosInstance;
|
|
26
|
-
public readonly emitter: LocusEmitter;
|
|
27
|
-
|
|
28
|
-
public readonly auth: AuthModule;
|
|
29
|
-
public readonly tasks: TasksModule;
|
|
30
|
-
public readonly sprints: SprintsModule;
|
|
31
|
-
public readonly workspaces: WorkspacesModule;
|
|
32
|
-
public readonly organizations: OrganizationsModule;
|
|
33
|
-
public readonly invitations: InvitationsModule;
|
|
34
|
-
public readonly docs: DocsModule;
|
|
35
|
-
public readonly ci: CiModule;
|
|
36
|
-
|
|
37
|
-
constructor(config: LocusConfig) {
|
|
38
|
-
this.emitter = new LocusEmitter();
|
|
39
|
-
|
|
40
|
-
this.api = axios.create({
|
|
41
|
-
baseURL: config.baseUrl,
|
|
42
|
-
timeout: config.timeout || 10000,
|
|
43
|
-
headers: {
|
|
44
|
-
"Content-Type": "application/json",
|
|
45
|
-
...(config.token ? { Authorization: `Bearer ${config.token}` } : {}),
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
this.setupInterceptors();
|
|
50
|
-
|
|
51
|
-
// Initialize modules
|
|
52
|
-
this.auth = new AuthModule(this.api, this.emitter);
|
|
53
|
-
this.tasks = new TasksModule(this.api, this.emitter);
|
|
54
|
-
this.sprints = new SprintsModule(this.api, this.emitter);
|
|
55
|
-
this.workspaces = new WorkspacesModule(this.api, this.emitter);
|
|
56
|
-
this.organizations = new OrganizationsModule(this.api, this.emitter);
|
|
57
|
-
this.invitations = new InvitationsModule(this.api, this.emitter);
|
|
58
|
-
this.docs = new DocsModule(this.api, this.emitter);
|
|
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
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private setupInterceptors() {
|
|
102
|
-
this.api.interceptors.response.use(
|
|
103
|
-
(response) => {
|
|
104
|
-
if (
|
|
105
|
-
response.data &&
|
|
106
|
-
typeof response.data === "object" &&
|
|
107
|
-
"data" in response.data
|
|
108
|
-
) {
|
|
109
|
-
response.data = response.data.data;
|
|
110
|
-
}
|
|
111
|
-
return response;
|
|
112
|
-
},
|
|
113
|
-
(error) => {
|
|
114
|
-
const status = error.response?.status;
|
|
115
|
-
|
|
116
|
-
// Extract error message from API response format: { error: { message: "..." } }
|
|
117
|
-
let message: string;
|
|
118
|
-
|
|
119
|
-
// Try to get message from API error response
|
|
120
|
-
if (
|
|
121
|
-
error.response?.data?.error?.message &&
|
|
122
|
-
typeof error.response.data.error.message === "string"
|
|
123
|
-
) {
|
|
124
|
-
message = error.response.data.error.message;
|
|
125
|
-
} else if (
|
|
126
|
-
error.response?.data?.message &&
|
|
127
|
-
typeof error.response.data.message === "string"
|
|
128
|
-
) {
|
|
129
|
-
message = error.response.data.message;
|
|
130
|
-
} else if (error.message && typeof error.message === "string") {
|
|
131
|
-
message = error.message;
|
|
132
|
-
} else {
|
|
133
|
-
message = "An error occurred";
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Create a new error with a meaningful message
|
|
137
|
-
const enhancedError = new Error(message);
|
|
138
|
-
enhancedError.name = `HTTP${status || "Error"}`;
|
|
139
|
-
|
|
140
|
-
if (status === 401) {
|
|
141
|
-
this.emitter.emit(LocusEvent.TOKEN_EXPIRED);
|
|
142
|
-
this.emitter.emit(LocusEvent.AUTH_ERROR, enhancedError);
|
|
143
|
-
} else {
|
|
144
|
-
this.emitter.emit(LocusEvent.REQUEST_ERROR, enhancedError);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return Promise.reject(enhancedError);
|
|
148
|
-
}
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
public setToken(token: string | null) {
|
|
153
|
-
if (token) {
|
|
154
|
-
this.api.defaults.headers.common.Authorization = `Bearer ${token}`;
|
|
155
|
-
} else {
|
|
156
|
-
delete this.api.defaults.headers.common.Authorization;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
package/src/modules/auth.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CompleteRegistration,
|
|
3
|
-
LoginResponse,
|
|
4
|
-
User,
|
|
5
|
-
VerifyOtp,
|
|
6
|
-
} from "@locusai/shared";
|
|
7
|
-
import { BaseModule } from "./base.js";
|
|
8
|
-
|
|
9
|
-
export class AuthModule extends BaseModule {
|
|
10
|
-
async getMe(): Promise<User> {
|
|
11
|
-
const { data } = await this.api.get<User>("/auth/me");
|
|
12
|
-
return data;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async requestRegisterOtp(email: string): Promise<{ success: boolean }> {
|
|
16
|
-
const { data } = await this.api.post<{ success: boolean }>(
|
|
17
|
-
"/auth/register-otp",
|
|
18
|
-
{ email }
|
|
19
|
-
);
|
|
20
|
-
return data;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async requestLoginOtp(email: string): Promise<{ success: boolean }> {
|
|
24
|
-
const { data } = await this.api.post<{ success: boolean }>(
|
|
25
|
-
"/auth/login-otp",
|
|
26
|
-
{ email }
|
|
27
|
-
);
|
|
28
|
-
return data;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async verifyLogin(body: VerifyOtp): Promise<LoginResponse> {
|
|
32
|
-
const { data } = await this.api.post<LoginResponse>(
|
|
33
|
-
"/auth/verify-login",
|
|
34
|
-
body
|
|
35
|
-
);
|
|
36
|
-
return data;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async completeRegistration(
|
|
40
|
-
body: CompleteRegistration
|
|
41
|
-
): Promise<LoginResponse> {
|
|
42
|
-
const { data } = await this.api.post<LoginResponse>(
|
|
43
|
-
"/auth/complete-registration",
|
|
44
|
-
body
|
|
45
|
-
);
|
|
46
|
-
return data;
|
|
47
|
-
}
|
|
48
|
-
}
|
package/src/modules/base.ts
DELETED
package/src/modules/ci.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { ReportCiResult } from "@locusai/shared";
|
|
2
|
-
import { BaseModule } from "./base.js";
|
|
3
|
-
|
|
4
|
-
export class CiModule extends BaseModule {
|
|
5
|
-
async report(body: ReportCiResult): Promise<{ success: boolean }> {
|
|
6
|
-
const { data } = await this.api.post<{ success: boolean }>(
|
|
7
|
-
"/ci/report",
|
|
8
|
-
body
|
|
9
|
-
);
|
|
10
|
-
return data;
|
|
11
|
-
}
|
|
12
|
-
}
|
package/src/modules/docs.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CreateDoc,
|
|
3
|
-
CreateDocGroup,
|
|
4
|
-
Doc,
|
|
5
|
-
DocGroup,
|
|
6
|
-
DocGroupResponse,
|
|
7
|
-
DocGroupsResponse,
|
|
8
|
-
DocResponse,
|
|
9
|
-
DocsResponse,
|
|
10
|
-
UpdateDoc,
|
|
11
|
-
UpdateDocGroup,
|
|
12
|
-
} from "@locusai/shared";
|
|
13
|
-
import { BaseModule } from "./base.js";
|
|
14
|
-
|
|
15
|
-
export class DocsModule extends BaseModule {
|
|
16
|
-
async create(workspaceId: string, body: CreateDoc): Promise<Doc> {
|
|
17
|
-
const { data } = await this.api.post<DocResponse>(
|
|
18
|
-
`/workspaces/${workspaceId}/docs`,
|
|
19
|
-
body
|
|
20
|
-
);
|
|
21
|
-
return data.doc;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async list(workspaceId: string): Promise<Doc[]> {
|
|
25
|
-
const { data } = await this.api.get<DocsResponse>(
|
|
26
|
-
`/workspaces/${workspaceId}/docs`
|
|
27
|
-
);
|
|
28
|
-
return data.docs;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async getById(id: string, workspaceId: string): Promise<Doc> {
|
|
32
|
-
const { data } = await this.api.get<DocResponse>(
|
|
33
|
-
`/workspaces/${workspaceId}/docs/${id}`
|
|
34
|
-
);
|
|
35
|
-
return data.doc;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async update(id: string, workspaceId: string, body: UpdateDoc): Promise<Doc> {
|
|
39
|
-
const { data } = await this.api.put<DocResponse>(
|
|
40
|
-
`/workspaces/${workspaceId}/docs/${id}`,
|
|
41
|
-
body
|
|
42
|
-
);
|
|
43
|
-
return data.doc;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async delete(id: string, workspaceId: string): Promise<void> {
|
|
47
|
-
await this.api.delete(`/workspaces/${workspaceId}/docs/${id}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Group Management
|
|
51
|
-
async listGroups(workspaceId: string): Promise<DocGroup[]> {
|
|
52
|
-
const { data } = await this.api.get<DocGroupsResponse>(
|
|
53
|
-
`/workspaces/${workspaceId}/doc-groups`
|
|
54
|
-
);
|
|
55
|
-
return data.groups;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async createGroup(
|
|
59
|
-
workspaceId: string,
|
|
60
|
-
body: CreateDocGroup
|
|
61
|
-
): Promise<DocGroup> {
|
|
62
|
-
const { data } = await this.api.post<DocGroupResponse>(
|
|
63
|
-
`/workspaces/${workspaceId}/doc-groups`,
|
|
64
|
-
body
|
|
65
|
-
);
|
|
66
|
-
return data.group;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async updateGroup(
|
|
70
|
-
id: string,
|
|
71
|
-
workspaceId: string,
|
|
72
|
-
body: UpdateDocGroup
|
|
73
|
-
): Promise<DocGroup> {
|
|
74
|
-
const { data } = await this.api.patch<DocGroupResponse>(
|
|
75
|
-
`/workspaces/${workspaceId}/doc-groups/${id}`,
|
|
76
|
-
body
|
|
77
|
-
);
|
|
78
|
-
return data.group;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async deleteGroup(id: string, workspaceId: string): Promise<void> {
|
|
82
|
-
await this.api.delete(`/workspaces/${workspaceId}/doc-groups/${id}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AcceptInvitation,
|
|
3
|
-
AcceptInvitationResponse,
|
|
4
|
-
CreateInvitation,
|
|
5
|
-
Invitation,
|
|
6
|
-
InvitationResponse,
|
|
7
|
-
InvitationsResponse,
|
|
8
|
-
} from "@locusai/shared";
|
|
9
|
-
import { BaseModule } from "./base.js";
|
|
10
|
-
|
|
11
|
-
export class InvitationsModule extends BaseModule {
|
|
12
|
-
async create(orgId: string, body: CreateInvitation): Promise<Invitation> {
|
|
13
|
-
const { data } = await this.api.post<InvitationResponse>(
|
|
14
|
-
`/org/${orgId}/invitations`,
|
|
15
|
-
body
|
|
16
|
-
);
|
|
17
|
-
return data.invitation;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async list(orgId: string): Promise<Invitation[]> {
|
|
21
|
-
const { data } = await this.api.get<InvitationsResponse>(
|
|
22
|
-
`/org/${orgId}/invitations`
|
|
23
|
-
);
|
|
24
|
-
return data.invitations;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async verify(token: string): Promise<InvitationResponse> {
|
|
28
|
-
const { data } = await this.api.get<InvitationResponse>(
|
|
29
|
-
`/invitations/verify/${token}`
|
|
30
|
-
);
|
|
31
|
-
return data;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async accept(body: AcceptInvitation): Promise<AcceptInvitationResponse> {
|
|
35
|
-
const { data } = await this.api.post<AcceptInvitationResponse>(
|
|
36
|
-
"/invitations/accept",
|
|
37
|
-
body
|
|
38
|
-
);
|
|
39
|
-
return data;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async revoke(orgId: string, id: string): Promise<void> {
|
|
43
|
-
await this.api.delete(`/org/${orgId}/invitations/${id}`);
|
|
44
|
-
}
|
|
45
|
-
}
|