@love-moon/conductor-sdk 0.2.32 → 0.2.33

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.
@@ -36,8 +36,14 @@ export declare class BackendApiError extends Error {
36
36
  export declare class ProjectSummary {
37
37
  readonly id: string;
38
38
  readonly name?: string | undefined;
39
- readonly description?: string | null | undefined;
40
- constructor(id: string, name?: string | undefined, description?: string | null | undefined);
39
+ readonly daemonHost?: string | null | undefined;
40
+ readonly workspacePath?: string | null | undefined;
41
+ readonly repoRoot?: string | null | undefined;
42
+ readonly worktreeBranch?: string | null | undefined;
43
+ readonly lastCommit?: string | null | undefined;
44
+ readonly fileCount?: number | null | undefined;
45
+ readonly isDefault?: boolean | undefined;
46
+ constructor(id: string, name?: string | undefined, daemonHost?: string | null | undefined, workspacePath?: string | null | undefined, repoRoot?: string | null | undefined, worktreeBranch?: string | null | undefined, lastCommit?: string | null | undefined, fileCount?: number | null | undefined, isDefault?: boolean | undefined);
41
47
  static fromJSON(payload: Record<string, any>): ProjectSummary;
42
48
  asObject(): Record<string, unknown>;
43
49
  }
@@ -62,9 +68,16 @@ export declare class BackendApiClient {
62
68
  constructor(config: ConductorConfig, options?: BackendClientOptions);
63
69
  listProjects(): Promise<ProjectSummary[]>;
64
70
  createProject(params: {
65
- name: string;
66
- description?: string;
71
+ name?: string;
67
72
  metadata?: Record<string, unknown>;
73
+ isDefault?: boolean;
74
+ bindingConfirmed?: boolean;
75
+ daemonHost?: string;
76
+ workspacePath?: string;
77
+ repoRoot?: string;
78
+ worktreeBranch?: string;
79
+ lastCommit?: string;
80
+ fileCount?: number;
68
81
  }): Promise<ProjectSummary>;
69
82
  listTasks(params?: {
70
83
  projectId?: string;
@@ -132,7 +145,9 @@ export declare class BackendApiClient {
132
145
  accepted?: boolean;
133
146
  }): Promise<Record<string, unknown>>;
134
147
  matchProjectByPath(params: {
135
- hostname: string;
148
+ hostname?: string;
149
+ daemonHost?: string;
150
+ daemon_host?: string;
136
151
  path: string;
137
152
  }): Promise<{
138
153
  project: ProjectSummary | null;
@@ -141,12 +156,25 @@ export declare class BackendApiClient {
141
156
  getProject(projectId: string): Promise<{
142
157
  id: string;
143
158
  name?: string;
144
- description?: string | null;
145
159
  metadata?: Record<string, unknown>;
160
+ daemonHost?: string | null;
161
+ workspacePath?: string | null;
162
+ repoRoot?: string | null;
163
+ worktreeBranch?: string | null;
164
+ lastCommit?: string | null;
165
+ fileCount?: number | null;
166
+ isDefault?: boolean;
146
167
  }>;
147
168
  updateProject(projectId: string, params: {
148
169
  name?: string;
149
170
  metadata?: Record<string, unknown>;
171
+ bindingConfirmed?: boolean;
172
+ daemonHost?: string;
173
+ workspacePath?: string;
174
+ repoRoot?: string;
175
+ worktreeBranch?: string;
176
+ lastCommit?: string;
177
+ fileCount?: number;
150
178
  }): Promise<ProjectSummary>;
151
179
  private request;
152
180
  private buildUrl;
@@ -10,24 +10,42 @@ export class BackendApiError extends Error {
10
10
  export class ProjectSummary {
11
11
  id;
12
12
  name;
13
- description;
14
- constructor(id, name, description) {
13
+ daemonHost;
14
+ workspacePath;
15
+ repoRoot;
16
+ worktreeBranch;
17
+ lastCommit;
18
+ fileCount;
19
+ isDefault;
20
+ constructor(id, name, daemonHost, workspacePath, repoRoot, worktreeBranch, lastCommit, fileCount, isDefault) {
15
21
  this.id = id;
16
22
  this.name = name;
17
- this.description = description;
23
+ this.daemonHost = daemonHost;
24
+ this.workspacePath = workspacePath;
25
+ this.repoRoot = repoRoot;
26
+ this.worktreeBranch = worktreeBranch;
27
+ this.lastCommit = lastCommit;
28
+ this.fileCount = fileCount;
29
+ this.isDefault = isDefault;
18
30
  }
19
31
  static fromJSON(payload) {
20
32
  const id = payload.id ? String(payload.id) : '';
21
33
  if (!id) {
22
34
  throw new Error('Project payload missing id');
23
35
  }
24
- return new ProjectSummary(id, payload.name ?? undefined, payload.description ?? undefined);
36
+ return new ProjectSummary(id, payload.name ?? undefined, payload.daemonHost ?? payload.daemon_host ?? null, payload.workspacePath ?? payload.workspace_path ?? null, payload.repoRoot ?? payload.repo_root ?? null, payload.worktreeBranch ?? payload.worktree_branch ?? null, payload.lastCommit ?? payload.last_commit ?? null, payload.fileCount ?? payload.file_count ?? null, payload.isDefault ?? payload.is_default ?? undefined);
25
37
  }
26
38
  asObject() {
27
39
  return {
28
40
  id: this.id,
29
41
  name: this.name,
30
- description: this.description,
42
+ daemonHost: this.daemonHost,
43
+ workspacePath: this.workspacePath,
44
+ repoRoot: this.repoRoot,
45
+ worktreeBranch: this.worktreeBranch,
46
+ lastCommit: this.lastCommit,
47
+ fileCount: this.fileCount,
48
+ isDefault: this.isDefault,
31
49
  };
32
50
  }
33
51
  }
@@ -270,8 +288,14 @@ export class BackendApiClient {
270
288
  return {
271
289
  id: payload.id,
272
290
  name: payload.name,
273
- description: payload.description,
274
291
  metadata: payload.metadata ?? undefined,
292
+ daemonHost: payload.daemonHost ?? payload.daemon_host ?? undefined,
293
+ workspacePath: payload.workspacePath ?? payload.workspace_path ?? undefined,
294
+ repoRoot: payload.repoRoot ?? payload.repo_root ?? undefined,
295
+ worktreeBranch: payload.worktreeBranch ?? payload.worktree_branch ?? undefined,
296
+ lastCommit: payload.lastCommit ?? payload.last_commit ?? undefined,
297
+ fileCount: payload.fileCount ?? payload.file_count ?? undefined,
298
+ isDefault: payload.isDefault ?? payload.is_default ?? undefined,
275
299
  };
276
300
  }
277
301
  async updateProject(projectId, params) {
package/dist/client.d.ts CHANGED
@@ -104,7 +104,8 @@ export declare class ConductorClient {
104
104
  receiveMessages(taskId: string, limit?: number): Promise<Record<string, any>>;
105
105
  ackMessages(taskId: string, ackToken?: string | null): Promise<Record<string, any> | undefined>;
106
106
  listProjects(): Promise<Record<string, any>>;
107
- createProject(name: string, description?: string, metadata?: Record<string, unknown>): Promise<Record<string, any>>;
107
+ createProject(name: string, metadata?: Record<string, unknown>): Promise<Record<string, any>>;
108
+ createProject(payload: Record<string, any>): Promise<Record<string, any>>;
108
109
  listTasks(payload?: Record<string, any>): Promise<Record<string, any>>;
109
110
  getLocalProjectRecord(payload?: Record<string, any>): Promise<Record<string, any>>;
110
111
  getLocalTaskRecord(payload?: Record<string, any>): Promise<Record<string, any>>;
package/dist/client.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
2
4
  import { BackendApiClient, BackendApiError } from './backend/index.js';
3
5
  import { loadConfig } from './config/index.js';
6
+ import { ProjectContext } from './context/index.js';
4
7
  import { getPlanLimitMessageFromError } from './limits/index.js';
5
8
  import { MessageRouter } from './message/index.js';
6
9
  import { DownstreamCursorStore, DurableUpstreamOutboxStore, normalizeDownstreamCommandCursor, } from './outbox/index.js';
@@ -338,12 +341,14 @@ export class ConductorClient {
338
341
  : {
339
342
  id: project.id,
340
343
  name: project.name ?? null,
341
- description: project.description ?? null,
342
344
  }),
343
345
  };
344
346
  }
345
- async createProject(name, description, metadata) {
346
- const project = await this.backendApi.createProject({ name, description, metadata });
347
+ async createProject(nameOrPayload, metadata) {
348
+ const payload = typeof nameOrPayload === 'string'
349
+ ? { name: nameOrPayload, metadata }
350
+ : { ...nameOrPayload };
351
+ const project = await this.backendApi.createProject(payload);
347
352
  return typeof project.asObject === 'function' ? project.asObject() : project;
348
353
  }
349
354
  async listTasks(payload = {}) {
@@ -456,12 +461,20 @@ export class ConductorClient {
456
461
  };
457
462
  }
458
463
  async matchProjectByPath(payload = {}) {
459
- const hostname = typeof payload.hostname === 'string' ? payload.hostname : currentHostname();
460
- const projectPath = typeof payload.project_path === 'string' && payload.project_path
464
+ const daemonHost = (typeof payload.daemon_host === 'string' && payload.daemon_host.trim()) ||
465
+ (typeof payload.daemonHost === 'string' && payload.daemonHost.trim()) ||
466
+ (typeof payload.hostname === 'string' && payload.hostname.trim()) ||
467
+ this.agentHost;
468
+ const projectPath = resolveWorkspacePath(typeof payload.project_path === 'string' && payload.project_path
461
469
  ? payload.project_path
462
- : this.projectPath;
470
+ : typeof payload.projectPath === 'string' && payload.projectPath
471
+ ? payload.projectPath
472
+ : this.projectPath);
473
+ if (!projectPath) {
474
+ throw new Error('project_path is required');
475
+ }
463
476
  const result = await this.backendApi.matchProjectByPath({
464
- hostname,
477
+ daemon_host: daemonHost,
465
478
  path: projectPath,
466
479
  });
467
480
  if (result.project) {
@@ -481,20 +494,36 @@ export class ConductorClient {
481
494
  if (!projectId) {
482
495
  throw new Error('project_id is required');
483
496
  }
484
- const hostname = typeof payload.hostname === 'string' ? payload.hostname : currentHostname();
485
- const projectPath = typeof payload.project_path === 'string' && payload.project_path
497
+ const daemonHost = (typeof payload.daemon_host === 'string' && payload.daemon_host.trim()) ||
498
+ (typeof payload.daemonHost === 'string' && payload.daemonHost.trim()) ||
499
+ (typeof payload.hostname === 'string' && payload.hostname.trim()) ||
500
+ this.agentHost;
501
+ const projectPath = resolveWorkspacePath(typeof payload.project_path === 'string' && payload.project_path
486
502
  ? payload.project_path
487
- : this.projectPath;
488
- const project = await this.backendApi.getProject(projectId);
489
- const metadata = (project.metadata || {});
490
- const localPaths = (metadata.localPaths || {});
491
- localPaths[hostname] = projectPath;
492
- metadata.localPaths = localPaths;
493
- await this.backendApi.updateProject(projectId, { metadata });
503
+ : typeof payload.projectPath === 'string' && payload.projectPath
504
+ ? payload.projectPath
505
+ : this.projectPath);
506
+ const snapshot = resolveWorkspaceSnapshot(projectPath);
507
+ const boundProject = await this.backendApi.updateProject(projectId, {
508
+ bindingConfirmed: true,
509
+ daemonHost,
510
+ workspacePath: snapshot.projectRoot,
511
+ repoRoot: snapshot.repoRoot,
512
+ worktreeBranch: snapshot.worktreeBranch,
513
+ lastCommit: snapshot.lastCommit,
514
+ fileCount: snapshot.fileCount,
515
+ });
494
516
  return {
495
517
  success: true,
496
- hostname,
497
- path: projectPath,
518
+ project_id: boundProject.id,
519
+ project_name: boundProject.name ?? null,
520
+ hostname: daemonHost,
521
+ daemon_host: daemonHost,
522
+ path: snapshot.projectRoot,
523
+ repo_root: snapshot.repoRoot ?? null,
524
+ worktree_branch: snapshot.worktreeBranch ?? null,
525
+ last_commit: snapshot.lastCommit ?? null,
526
+ file_count: snapshot.fileCount ?? null,
498
527
  };
499
528
  }
500
529
  handleBackendEvent = async (payload) => {
@@ -906,3 +935,26 @@ function normalizeDeliveryScopeId(scopeId) {
906
935
  const normalized = String(scopeId || '').trim();
907
936
  return normalized || 'default';
908
937
  }
938
+ function resolveWorkspacePath(candidate) {
939
+ const trimmed = typeof candidate === 'string' ? candidate.trim() : '';
940
+ if (!trimmed) {
941
+ return '';
942
+ }
943
+ try {
944
+ return fs.realpathSync(trimmed);
945
+ }
946
+ catch {
947
+ return path.resolve(trimmed);
948
+ }
949
+ }
950
+ function resolveWorkspaceSnapshot(projectPath) {
951
+ try {
952
+ const context = new ProjectContext(projectPath);
953
+ return context.snapshot();
954
+ }
955
+ catch {
956
+ return {
957
+ projectRoot: resolveWorkspacePath(projectPath),
958
+ };
959
+ }
960
+ }
@@ -2,13 +2,24 @@ export interface GuessResult {
2
2
  projectRoot: string;
3
3
  repoRoot?: string;
4
4
  }
5
+ export interface WorkspaceSnapshot {
6
+ projectRoot: string;
7
+ repoRoot?: string;
8
+ worktreeBranch?: string;
9
+ lastCommit?: string;
10
+ fileCount?: number;
11
+ }
5
12
  export declare class ProjectContext {
6
13
  private readonly root;
7
14
  constructor(targetPath?: string);
8
15
  guess(): GuessResult;
16
+ snapshot(): WorkspaceSnapshot;
9
17
  listFiles(relativeToRepo?: boolean): string[];
10
18
  readFile(relativePath: string): string;
11
19
  getDiff(staged?: boolean): string;
12
20
  private gitRoot;
21
+ private gitBranch;
22
+ private gitHead;
23
+ private gitFileCount;
13
24
  private gitListFiles;
14
25
  }
@@ -14,6 +14,21 @@ export class ProjectContext {
14
14
  repoRoot: repoRoot ?? undefined,
15
15
  };
16
16
  }
17
+ snapshot() {
18
+ const guess = this.guess();
19
+ if (!guess.repoRoot) {
20
+ return {
21
+ projectRoot: guess.projectRoot,
22
+ };
23
+ }
24
+ return {
25
+ projectRoot: guess.projectRoot,
26
+ repoRoot: guess.repoRoot,
27
+ worktreeBranch: this.gitBranch(guess.repoRoot) ?? undefined,
28
+ lastCommit: this.gitHead(guess.repoRoot) ?? undefined,
29
+ fileCount: this.gitFileCount(guess.repoRoot) ?? undefined,
30
+ };
31
+ }
17
32
  listFiles(relativeToRepo = true) {
18
33
  const guess = this.guess();
19
34
  if (guess.repoRoot) {
@@ -55,6 +70,35 @@ export class ProjectContext {
55
70
  return null;
56
71
  }
57
72
  }
73
+ gitBranch(repoRoot) {
74
+ try {
75
+ const branch = runGit(['rev-parse', '--abbrev-ref', 'HEAD'], repoRoot).trim();
76
+ if (!branch || branch === 'HEAD') {
77
+ return null;
78
+ }
79
+ return branch;
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ }
85
+ gitHead(repoRoot) {
86
+ try {
87
+ const head = runGit(['rev-parse', 'HEAD'], repoRoot).trim();
88
+ return head || null;
89
+ }
90
+ catch {
91
+ return null;
92
+ }
93
+ }
94
+ gitFileCount(repoRoot) {
95
+ try {
96
+ return this.gitListFiles(repoRoot).length;
97
+ }
98
+ catch {
99
+ return null;
100
+ }
101
+ }
58
102
  gitListFiles(repoRoot) {
59
103
  try {
60
104
  const output = runGit(['ls-files'], repoRoot);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@love-moon/conductor-sdk",
3
- "version": "0.2.32",
3
+ "version": "0.2.33",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,5 +27,5 @@
27
27
  "typescript": "^5.6.3",
28
28
  "vitest": "^2.1.4"
29
29
  },
30
- "gitCommitId": "c749d4b"
30
+ "gitCommitId": "db7f9bf"
31
31
  }