forkit-connect 0.1.5 → 0.1.6
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/QUICKSTART.md +10 -1
- package/README.md +12 -1
- package/dist/cli.js +763 -37
- package/dist/launcher.js +471 -73
- package/dist/resource-meter.d.ts +15 -0
- package/dist/resource-meter.js +60 -0
- package/dist/v1/api.d.ts +41 -0
- package/dist/v1/api.js +57 -0
- package/dist/v1/daemon.js +9 -0
- package/dist/v1/repo-discovery.d.ts +42 -0
- package/dist/v1/repo-discovery.js +206 -0
- package/dist/v1/runtime-activity.d.ts +55 -0
- package/dist/v1/runtime-activity.js +388 -0
- package/dist/v1/runtime-context.d.ts +18 -0
- package/dist/v1/runtime-context.js +96 -0
- package/dist/v1/runtime-editor-activity.d.ts +47 -0
- package/dist/v1/runtime-editor-activity.js +821 -0
- package/dist/v1/runtime-observation-runner.d.ts +49 -0
- package/dist/v1/runtime-observation-runner.js +508 -0
- package/dist/v1/runtime-observer.d.ts +50 -0
- package/dist/v1/runtime-observer.js +867 -0
- package/dist/v1/runtime-registration.d.ts +58 -0
- package/dist/v1/runtime-registration.js +319 -0
- package/dist/v1/service.d.ts +44 -1
- package/dist/v1/service.js +165 -10
- package/dist/v1/state.d.ts +4 -1
- package/dist/v1/state.js +28 -0
- package/dist/v1/types.d.ts +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface HardwareMetrics {
|
|
2
|
+
cpu_percent: number;
|
|
3
|
+
vram_mb: number;
|
|
4
|
+
}
|
|
5
|
+
export declare class ResourceMeter {
|
|
6
|
+
constructor();
|
|
7
|
+
private snapshotCpuTicks;
|
|
8
|
+
private measureCpuPercent;
|
|
9
|
+
/**
|
|
10
|
+
* Captures generalized CPU & VRAM metadata for the system bounds.
|
|
11
|
+
* Native mapping to 'nvidia-smi' exposes exact VRAM allocations.
|
|
12
|
+
*/
|
|
13
|
+
captureMetrics(targetProcessPath?: string): Promise<HardwareMetrics>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=resource-meter.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ResourceMeter = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
9
|
+
const util_1 = require("util");
|
|
10
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
11
|
+
class ResourceMeter {
|
|
12
|
+
constructor() { }
|
|
13
|
+
snapshotCpuTicks() {
|
|
14
|
+
const cores = node_os_1.default.cpus();
|
|
15
|
+
let idle = 0;
|
|
16
|
+
let total = 0;
|
|
17
|
+
for (const core of cores) {
|
|
18
|
+
const times = core.times;
|
|
19
|
+
idle += times.idle;
|
|
20
|
+
total += times.user + times.nice + times.sys + times.idle + times.irq;
|
|
21
|
+
}
|
|
22
|
+
return { idle, total };
|
|
23
|
+
}
|
|
24
|
+
async measureCpuPercent(sampleMs = 180) {
|
|
25
|
+
const start = this.snapshotCpuTicks();
|
|
26
|
+
await new Promise((resolve) => setTimeout(resolve, sampleMs));
|
|
27
|
+
const end = this.snapshotCpuTicks();
|
|
28
|
+
const idleDelta = end.idle - start.idle;
|
|
29
|
+
const totalDelta = end.total - start.total;
|
|
30
|
+
if (totalDelta <= 0)
|
|
31
|
+
return 0;
|
|
32
|
+
const busyRatio = 1 - (idleDelta / totalDelta);
|
|
33
|
+
return Math.max(0, Math.min(100, busyRatio * 100));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Captures generalized CPU & VRAM metadata for the system bounds.
|
|
37
|
+
* Native mapping to 'nvidia-smi' exposes exact VRAM allocations.
|
|
38
|
+
*/
|
|
39
|
+
async captureMetrics(targetProcessPath) {
|
|
40
|
+
let vram_mb = 0;
|
|
41
|
+
let cpu_percent = 0;
|
|
42
|
+
try {
|
|
43
|
+
cpu_percent = await this.measureCpuPercent();
|
|
44
|
+
// Attempt explicit native `nvidia-smi` read
|
|
45
|
+
const { stdout } = await execAsync('nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits');
|
|
46
|
+
if (stdout && stdout.trim()) {
|
|
47
|
+
const mems = stdout.split('\n').map(m => parseInt(m.trim())).filter(m => !isNaN(m));
|
|
48
|
+
if (mems.length > 0) {
|
|
49
|
+
vram_mb = mems.reduce((a, b) => a + b, 0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
// Graceful drop to zero if no explicit hardware bindings exist
|
|
55
|
+
}
|
|
56
|
+
return { cpu_percent, vram_mb };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.ResourceMeter = ResourceMeter;
|
|
60
|
+
//# sourceMappingURL=resource-meter.js.map
|
package/dist/v1/api.d.ts
CHANGED
|
@@ -95,6 +95,7 @@ export interface ProductSummaryResponse {
|
|
|
95
95
|
} | null;
|
|
96
96
|
runtimeSignals?: {
|
|
97
97
|
runtimeSignalsPerMonth?: number | null;
|
|
98
|
+
maxRuntimeSubjects?: number | null;
|
|
98
99
|
} | null;
|
|
99
100
|
} | null;
|
|
100
101
|
entitlements?: {
|
|
@@ -105,6 +106,7 @@ export interface ProductSummaryResponse {
|
|
|
105
106
|
maxProjects?: number | null;
|
|
106
107
|
maxWorkspaces?: number | null;
|
|
107
108
|
maxGovernedPassports?: number | null;
|
|
109
|
+
maxRuntimeSubjects?: number | null;
|
|
108
110
|
} | null;
|
|
109
111
|
usage?: {
|
|
110
112
|
passports?: number | null;
|
|
@@ -114,6 +116,41 @@ export interface ProductSummaryResponse {
|
|
|
114
116
|
runtimeSignals?: number | null;
|
|
115
117
|
} | null;
|
|
116
118
|
}
|
|
119
|
+
export interface RuntimeResponse {
|
|
120
|
+
id?: number | string | null;
|
|
121
|
+
gaid?: string | null;
|
|
122
|
+
name?: string | null;
|
|
123
|
+
passportType?: string | null;
|
|
124
|
+
workspaceId?: string | null;
|
|
125
|
+
projectId?: string | null;
|
|
126
|
+
connectionStatus?: string | null;
|
|
127
|
+
sourceUrl?: string | null;
|
|
128
|
+
metadata?: Record<string, unknown> | null;
|
|
129
|
+
}
|
|
130
|
+
export interface RuntimeListResponse {
|
|
131
|
+
runtimes?: RuntimeResponse[];
|
|
132
|
+
total?: number | null;
|
|
133
|
+
runtimeLimit?: number | null;
|
|
134
|
+
usage?: {
|
|
135
|
+
runtimeSubjects?: number | null;
|
|
136
|
+
} | null;
|
|
137
|
+
}
|
|
138
|
+
export interface RuntimeCreateResponse {
|
|
139
|
+
runtime?: RuntimeResponse | null;
|
|
140
|
+
tier?: string | null;
|
|
141
|
+
runtimeLimit?: number | null;
|
|
142
|
+
usage?: {
|
|
143
|
+
runtimeSubjects?: number | null;
|
|
144
|
+
} | null;
|
|
145
|
+
}
|
|
146
|
+
export interface ApiKeyCreateResponse {
|
|
147
|
+
gaid?: string | null;
|
|
148
|
+
keyId?: string | null;
|
|
149
|
+
label?: string | null;
|
|
150
|
+
createdAt?: string | null;
|
|
151
|
+
rawKey?: string | null;
|
|
152
|
+
_warning?: string | null;
|
|
153
|
+
}
|
|
117
154
|
export interface WorkspaceProject {
|
|
118
155
|
id?: string | null;
|
|
119
156
|
workspaceId?: string | null;
|
|
@@ -294,6 +331,8 @@ export declare class ConnectApiClient {
|
|
|
294
331
|
getPassportsMine(limit?: number): Promise<ApiCallResult>;
|
|
295
332
|
getProductSummary(): Promise<ApiCallResult>;
|
|
296
333
|
createPassportDraft(payload: Record<string, unknown>): Promise<ApiCallResult>;
|
|
334
|
+
getRuntimesMine(limit?: number): Promise<ApiCallResult>;
|
|
335
|
+
createRuntime(payload: Record<string, unknown>): Promise<ApiCallResult>;
|
|
297
336
|
pushRuntimeSignalEvent(payload: Record<string, unknown>): Promise<ApiCallResult>;
|
|
298
337
|
pushRuntimeRunLog(payload: Record<string, unknown>): Promise<ApiCallResult>;
|
|
299
338
|
getDeployments(gaid: string, scope?: DeploymentScopeQuery): Promise<ApiCallResult>;
|
|
@@ -305,6 +344,7 @@ export declare class ConnectApiClient {
|
|
|
305
344
|
resumeDeploymentSession(gaid: string, sessionId: string, payload?: DeploymentSessionControlRequest, scope?: DeploymentScopeQuery): Promise<ApiCallResult>;
|
|
306
345
|
revokeDeploymentSession(gaid: string, sessionId: string, payload?: DeploymentSessionControlRequest, scope?: DeploymentScopeQuery): Promise<ApiCallResult>;
|
|
307
346
|
sendDeploymentCheckin(gaid: string, payload: DeploymentCheckinRequest): Promise<ApiCallResult>;
|
|
347
|
+
sendRuntimeCheckin(gaid: string, payload: DeploymentCheckinRequest, sessionId?: string | null): Promise<ApiCallResult>;
|
|
308
348
|
getProfileAccess(): Promise<ApiCallResult>;
|
|
309
349
|
getWorkspaceProjects(workspaceId: string): Promise<ApiCallResult>;
|
|
310
350
|
createWorkspace(payload: {
|
|
@@ -332,6 +372,7 @@ export declare class ConnectApiClient {
|
|
|
332
372
|
* owned passport. Returns the rawKey exactly once — caller must persist it.
|
|
333
373
|
*/
|
|
334
374
|
createPassportApiKey(gaid: string, label?: string): Promise<ApiCallResult>;
|
|
375
|
+
createRuntimeApiKey(gaid: string, label?: string): Promise<ApiCallResult>;
|
|
335
376
|
private parseBody;
|
|
336
377
|
}
|
|
337
378
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/v1/api.js
CHANGED
|
@@ -138,6 +138,34 @@ class ConnectApiClient {
|
|
|
138
138
|
contract: this.parseContract(res),
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
|
+
async getRuntimesMine(limit = 100) {
|
|
142
|
+
const safeLimit = Math.min(Math.max(Math.trunc(Number(limit) || 100), 1), 100);
|
|
143
|
+
const url = `${this.config.baseUrl}/api/v1/runtimes/mine?limit=${safeLimit}`;
|
|
144
|
+
const res = await fetch(url, {
|
|
145
|
+
method: 'GET',
|
|
146
|
+
headers: this.getHeaders(),
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
ok: res.ok,
|
|
150
|
+
status: res.status,
|
|
151
|
+
body: await this.parseBody(res),
|
|
152
|
+
contract: this.parseContract(res),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async createRuntime(payload) {
|
|
156
|
+
const url = `${this.config.baseUrl}/api/v1/runtimes`;
|
|
157
|
+
const res = await fetch(url, {
|
|
158
|
+
method: 'POST',
|
|
159
|
+
headers: this.getHeaders(),
|
|
160
|
+
body: JSON.stringify(payload),
|
|
161
|
+
});
|
|
162
|
+
return {
|
|
163
|
+
ok: res.ok,
|
|
164
|
+
status: res.status,
|
|
165
|
+
body: await this.parseBody(res),
|
|
166
|
+
contract: this.parseContract(res),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
141
169
|
async pushRuntimeSignalEvent(payload) {
|
|
142
170
|
const url = `${this.config.baseUrl}/api/v1/runtime-signals/events`;
|
|
143
171
|
const res = await fetch(url, {
|
|
@@ -278,6 +306,21 @@ class ConnectApiClient {
|
|
|
278
306
|
contract: this.parseContract(res),
|
|
279
307
|
};
|
|
280
308
|
}
|
|
309
|
+
async sendRuntimeCheckin(gaid, payload, sessionId) {
|
|
310
|
+
const suffix = sessionId ? `/${encodeURIComponent(sessionId)}` : '';
|
|
311
|
+
const url = `${this.config.baseUrl}/api/v1/runtimes/${encodeURIComponent(gaid)}/checkins${suffix}`;
|
|
312
|
+
const res = await fetch(url, {
|
|
313
|
+
method: 'POST',
|
|
314
|
+
headers: this.getHeaders(),
|
|
315
|
+
body: JSON.stringify(payload),
|
|
316
|
+
});
|
|
317
|
+
return {
|
|
318
|
+
ok: res.ok,
|
|
319
|
+
status: res.status,
|
|
320
|
+
body: await this.parseBody(res),
|
|
321
|
+
contract: this.parseContract(res),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
281
324
|
async getProfileAccess() {
|
|
282
325
|
const url = `${this.config.baseUrl}/api/profiles/access`;
|
|
283
326
|
const res = await fetch(url, {
|
|
@@ -507,6 +550,20 @@ class ConnectApiClient {
|
|
|
507
550
|
contract: this.parseContract(res),
|
|
508
551
|
};
|
|
509
552
|
}
|
|
553
|
+
async createRuntimeApiKey(gaid, label) {
|
|
554
|
+
const url = `${this.config.baseUrl}/api/v1/runtimes/${encodeURIComponent(gaid)}/api-keys`;
|
|
555
|
+
const res = await fetch(url, {
|
|
556
|
+
method: 'POST',
|
|
557
|
+
headers: this.getHeaders(),
|
|
558
|
+
body: JSON.stringify({ label: label ?? 'Forkit Connect runtime' }),
|
|
559
|
+
});
|
|
560
|
+
return {
|
|
561
|
+
ok: res.ok,
|
|
562
|
+
status: res.status,
|
|
563
|
+
body: await this.parseBody(res),
|
|
564
|
+
contract: this.parseContract(res),
|
|
565
|
+
};
|
|
566
|
+
}
|
|
510
567
|
async parseBody(res) {
|
|
511
568
|
const contentType = res.headers.get('content-type') ?? '';
|
|
512
569
|
if (contentType.includes('application/json')) {
|
package/dist/v1/daemon.js
CHANGED
|
@@ -10,6 +10,7 @@ exports.stopDaemonBackground = stopDaemonBackground;
|
|
|
10
10
|
exports.restartDaemonBackground = restartDaemonBackground;
|
|
11
11
|
const node_child_process_1 = require("node:child_process");
|
|
12
12
|
const heartbeat_1 = require("./heartbeat");
|
|
13
|
+
const runtime_observation_runner_1 = require("./runtime-observation-runner");
|
|
13
14
|
const DEFAULT_DAEMON_WAIT_MS = 4000;
|
|
14
15
|
const DAEMON_POLL_INTERVAL_MS = 100;
|
|
15
16
|
function sleep(ms) {
|
|
@@ -61,6 +62,14 @@ async function runDaemonScanCycle(service, options) {
|
|
|
61
62
|
}
|
|
62
63
|
await service.syncC2SessionsFromBackend({ suppressErrors: true });
|
|
63
64
|
await service.flushC2LifecycleEvents({ suppressErrors: true });
|
|
65
|
+
try {
|
|
66
|
+
await (0, runtime_observation_runner_1.observeRegisteredRuntimeTargets)(service, {
|
|
67
|
+
...(options?.agentProcessEntries ? { processEntries: options.agentProcessEntries } : {}),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Runtime journaling is additive; daemon discovery and heartbeat must continue even if observer reads fail.
|
|
72
|
+
}
|
|
64
73
|
service.deliverPendingNotifications(options?.notificationSender ? { notificationSender: options.notificationSender } : undefined);
|
|
65
74
|
return result;
|
|
66
75
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type RepoHostKind = 'github' | 'gitlab' | 'bitbucket' | 'azure-devops' | 'other';
|
|
2
|
+
export interface GitRemote {
|
|
3
|
+
name: string;
|
|
4
|
+
fetchUrl: string | null;
|
|
5
|
+
pushUrl: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface RepoDetectionResult {
|
|
8
|
+
cwd: string;
|
|
9
|
+
isGitRepo: boolean;
|
|
10
|
+
rootPath: string | null;
|
|
11
|
+
relativeCwd: string | null;
|
|
12
|
+
branch: string | null;
|
|
13
|
+
headCommit: string | null;
|
|
14
|
+
remotes: GitRemote[];
|
|
15
|
+
preferredRemote: GitRemote | null;
|
|
16
|
+
sourceHost: RepoHostKind | null;
|
|
17
|
+
repoSlug: string | null;
|
|
18
|
+
dirty: boolean | null;
|
|
19
|
+
}
|
|
20
|
+
export interface RepoCommandResult {
|
|
21
|
+
status: number | null;
|
|
22
|
+
stdout: string;
|
|
23
|
+
stderr: string;
|
|
24
|
+
}
|
|
25
|
+
export type RepoCommandRunner = (command: string, args: string[], options?: {
|
|
26
|
+
cwd?: string;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
}) => RepoCommandResult;
|
|
29
|
+
export declare function parseRemoteUrl(input: string): {
|
|
30
|
+
host: RepoHostKind;
|
|
31
|
+
slug: string | null;
|
|
32
|
+
};
|
|
33
|
+
export declare function classifyRepoHost(hostname: string): RepoHostKind;
|
|
34
|
+
export declare function parseGitRemoteOutput(output: string): GitRemote[];
|
|
35
|
+
export declare function parseGitStatusFiles(output: string): string[];
|
|
36
|
+
export declare function defaultRepoCommandRunner(command: string, args: string[], options?: {
|
|
37
|
+
cwd?: string;
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
}): RepoCommandResult;
|
|
40
|
+
export declare function detectRepository(cwd?: string, runner?: RepoCommandRunner): RepoDetectionResult;
|
|
41
|
+
export declare function listRepositoryChangedFiles(cwd?: string, runner?: RepoCommandRunner): string[];
|
|
42
|
+
//# sourceMappingURL=repo-discovery.d.ts.map
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseRemoteUrl = parseRemoteUrl;
|
|
7
|
+
exports.classifyRepoHost = classifyRepoHost;
|
|
8
|
+
exports.parseGitRemoteOutput = parseGitRemoteOutput;
|
|
9
|
+
exports.parseGitStatusFiles = parseGitStatusFiles;
|
|
10
|
+
exports.defaultRepoCommandRunner = defaultRepoCommandRunner;
|
|
11
|
+
exports.detectRepository = detectRepository;
|
|
12
|
+
exports.listRepositoryChangedFiles = listRepositoryChangedFiles;
|
|
13
|
+
const node_child_process_1 = require("node:child_process");
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
function trimTrailingGitSuffix(value) {
|
|
16
|
+
return value.replace(/\.git$/i, '').replace(/\/+$/, '');
|
|
17
|
+
}
|
|
18
|
+
function normalizeRelativeRepoPath(value) {
|
|
19
|
+
const normalized = String(value || '').trim().replace(/\\/g, '/').replace(/^\/+/, '');
|
|
20
|
+
if (!normalized || normalized === '.')
|
|
21
|
+
return null;
|
|
22
|
+
return normalized;
|
|
23
|
+
}
|
|
24
|
+
function normalizeRepoPath(value) {
|
|
25
|
+
return trimTrailingGitSuffix(value.replace(/\\/g, '/'));
|
|
26
|
+
}
|
|
27
|
+
function parseRemoteUrl(input) {
|
|
28
|
+
const raw = String(input || '').trim();
|
|
29
|
+
if (!raw) {
|
|
30
|
+
return { host: 'other', slug: null };
|
|
31
|
+
}
|
|
32
|
+
const sshMatch = raw.match(/^[^@]+@([^:]+):(.+)$/);
|
|
33
|
+
if (sshMatch) {
|
|
34
|
+
const host = classifyRepoHost(sshMatch[1] || '');
|
|
35
|
+
return {
|
|
36
|
+
host,
|
|
37
|
+
slug: normalizeRepoPath(sshMatch[2] || '') || null,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const url = new URL(raw);
|
|
42
|
+
const host = classifyRepoHost(url.hostname);
|
|
43
|
+
let pathname = normalizeRepoPath(url.pathname || '');
|
|
44
|
+
pathname = pathname.replace(/^\/+/, '');
|
|
45
|
+
if (host === 'azure-devops') {
|
|
46
|
+
const parts = pathname.split('/').filter(Boolean);
|
|
47
|
+
const devopsIndex = parts.findIndex((part) => part.toLowerCase() === '_git');
|
|
48
|
+
if (devopsIndex > 0 && devopsIndex < parts.length - 1) {
|
|
49
|
+
return {
|
|
50
|
+
host,
|
|
51
|
+
slug: `${parts.slice(0, devopsIndex).join('/')}/${parts[devopsIndex + 1]}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
host,
|
|
57
|
+
slug: pathname || null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return {
|
|
62
|
+
host: 'other',
|
|
63
|
+
slug: null,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function classifyRepoHost(hostname) {
|
|
68
|
+
const normalized = String(hostname || '').trim().toLowerCase().replace(/^www\./, '');
|
|
69
|
+
if (!normalized)
|
|
70
|
+
return 'other';
|
|
71
|
+
if (normalized === 'github.com')
|
|
72
|
+
return 'github';
|
|
73
|
+
if (normalized === 'gitlab.com' || normalized.endsWith('.gitlab.com'))
|
|
74
|
+
return 'gitlab';
|
|
75
|
+
if (normalized === 'bitbucket.org' || normalized.endsWith('.bitbucket.org'))
|
|
76
|
+
return 'bitbucket';
|
|
77
|
+
if (normalized === 'dev.azure.com' || normalized.endsWith('.visualstudio.com'))
|
|
78
|
+
return 'azure-devops';
|
|
79
|
+
return 'other';
|
|
80
|
+
}
|
|
81
|
+
function parseGitRemoteOutput(output) {
|
|
82
|
+
const byName = new Map();
|
|
83
|
+
for (const rawLine of String(output || '').split(/\r?\n/)) {
|
|
84
|
+
const line = rawLine.trim();
|
|
85
|
+
if (!line)
|
|
86
|
+
continue;
|
|
87
|
+
const match = line.match(/^(\S+)\s+(\S+)\s+\((fetch|push)\)$/);
|
|
88
|
+
if (!match)
|
|
89
|
+
continue;
|
|
90
|
+
const name = match[1] || '';
|
|
91
|
+
const url = match[2] || '';
|
|
92
|
+
const kind = match[3] || '';
|
|
93
|
+
const current = byName.get(name) || {
|
|
94
|
+
name,
|
|
95
|
+
fetchUrl: null,
|
|
96
|
+
pushUrl: null,
|
|
97
|
+
};
|
|
98
|
+
if (kind === 'fetch')
|
|
99
|
+
current.fetchUrl = url;
|
|
100
|
+
if (kind === 'push')
|
|
101
|
+
current.pushUrl = url;
|
|
102
|
+
byName.set(name, current);
|
|
103
|
+
}
|
|
104
|
+
return [...byName.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
105
|
+
}
|
|
106
|
+
function parseGitStatusFiles(output) {
|
|
107
|
+
const files = [];
|
|
108
|
+
const seen = new Set();
|
|
109
|
+
for (const rawLine of String(output || '').split(/\r?\n/)) {
|
|
110
|
+
const line = rawLine.trimEnd();
|
|
111
|
+
if (!line)
|
|
112
|
+
continue;
|
|
113
|
+
const payload = line.slice(3).trim();
|
|
114
|
+
if (!payload)
|
|
115
|
+
continue;
|
|
116
|
+
const renamedTarget = payload.includes('->') ? payload.split('->').pop() || '' : payload;
|
|
117
|
+
const normalized = normalizeRelativeRepoPath(renamedTarget.trim());
|
|
118
|
+
if (!normalized || seen.has(normalized))
|
|
119
|
+
continue;
|
|
120
|
+
seen.add(normalized);
|
|
121
|
+
files.push(normalized);
|
|
122
|
+
}
|
|
123
|
+
return files;
|
|
124
|
+
}
|
|
125
|
+
function selectPreferredRemote(remotes) {
|
|
126
|
+
if (!remotes.length)
|
|
127
|
+
return null;
|
|
128
|
+
return remotes.find((remote) => remote.name === 'origin') || remotes[0] || null;
|
|
129
|
+
}
|
|
130
|
+
function defaultRepoCommandRunner(command, args, options) {
|
|
131
|
+
const result = (0, node_child_process_1.spawnSync)(command, args, {
|
|
132
|
+
cwd: options?.cwd,
|
|
133
|
+
encoding: 'utf8',
|
|
134
|
+
timeout: options?.timeoutMs ?? 4000,
|
|
135
|
+
windowsHide: true,
|
|
136
|
+
});
|
|
137
|
+
return {
|
|
138
|
+
status: result.status,
|
|
139
|
+
stdout: String(result.stdout || ''),
|
|
140
|
+
stderr: String(result.stderr || ''),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function runGit(cwd, args, runner) {
|
|
144
|
+
return runner('git', args, { cwd, timeoutMs: 4000 });
|
|
145
|
+
}
|
|
146
|
+
function detectRepository(cwd = process.cwd(), runner = defaultRepoCommandRunner) {
|
|
147
|
+
const normalizedCwd = node_path_1.default.resolve(cwd);
|
|
148
|
+
const rootResult = runGit(normalizedCwd, ['rev-parse', '--show-toplevel'], runner);
|
|
149
|
+
if (rootResult.status !== 0) {
|
|
150
|
+
return {
|
|
151
|
+
cwd: normalizedCwd,
|
|
152
|
+
isGitRepo: false,
|
|
153
|
+
rootPath: null,
|
|
154
|
+
relativeCwd: null,
|
|
155
|
+
branch: null,
|
|
156
|
+
headCommit: null,
|
|
157
|
+
remotes: [],
|
|
158
|
+
preferredRemote: null,
|
|
159
|
+
sourceHost: null,
|
|
160
|
+
repoSlug: null,
|
|
161
|
+
dirty: null,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const rootPath = String(rootResult.stdout || '').trim() || null;
|
|
165
|
+
const branchResult = rootPath
|
|
166
|
+
? runGit(rootPath, ['branch', '--show-current'], runner)
|
|
167
|
+
: { status: 1, stdout: '', stderr: '' };
|
|
168
|
+
const headCommitResult = rootPath
|
|
169
|
+
? runGit(rootPath, ['rev-parse', 'HEAD'], runner)
|
|
170
|
+
: { status: 1, stdout: '', stderr: '' };
|
|
171
|
+
const remoteResult = rootPath
|
|
172
|
+
? runGit(rootPath, ['remote', '-v'], runner)
|
|
173
|
+
: { status: 1, stdout: '', stderr: '' };
|
|
174
|
+
const dirtyResult = rootPath
|
|
175
|
+
? runGit(rootPath, ['status', '--porcelain', '--untracked-files=no'], runner)
|
|
176
|
+
: { status: 1, stdout: '', stderr: '' };
|
|
177
|
+
const remotes = parseGitRemoteOutput(remoteResult.stdout);
|
|
178
|
+
const preferredRemote = selectPreferredRemote(remotes);
|
|
179
|
+
const preferredRemoteUrl = preferredRemote?.fetchUrl || preferredRemote?.pushUrl || '';
|
|
180
|
+
const preferredRemoteInfo = parseRemoteUrl(preferredRemoteUrl);
|
|
181
|
+
return {
|
|
182
|
+
cwd: normalizedCwd,
|
|
183
|
+
isGitRepo: true,
|
|
184
|
+
rootPath,
|
|
185
|
+
relativeCwd: rootPath ? normalizeRelativeRepoPath(node_path_1.default.relative(rootPath, normalizedCwd)) : null,
|
|
186
|
+
branch: branchResult.status === 0 ? String(branchResult.stdout || '').trim() || null : null,
|
|
187
|
+
headCommit: headCommitResult.status === 0 ? String(headCommitResult.stdout || '').trim() || null : null,
|
|
188
|
+
remotes,
|
|
189
|
+
preferredRemote,
|
|
190
|
+
sourceHost: preferredRemote ? preferredRemoteInfo.host : null,
|
|
191
|
+
repoSlug: preferredRemote ? preferredRemoteInfo.slug : null,
|
|
192
|
+
dirty: dirtyResult.status === 0 ? String(dirtyResult.stdout || '').trim().length > 0 : null,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function listRepositoryChangedFiles(cwd = process.cwd(), runner = defaultRepoCommandRunner) {
|
|
196
|
+
const repo = detectRepository(cwd, runner);
|
|
197
|
+
if (!repo.isGitRepo || !repo.rootPath) {
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
const statusResult = runGit(repo.rootPath, ['status', '--porcelain'], runner);
|
|
201
|
+
if (statusResult.status !== 0) {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
return parseGitStatusFiles(statusResult.stdout);
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=repo-discovery.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { RepoDetectionResult } from './repo-discovery';
|
|
2
|
+
export interface RuntimeActivityResourcesInput {
|
|
3
|
+
cpuPercent?: number | undefined;
|
|
4
|
+
memoryMb?: number | undefined;
|
|
5
|
+
vramMb?: number | undefined;
|
|
6
|
+
}
|
|
7
|
+
export interface RuntimeActivityThreadInput {
|
|
8
|
+
title: string;
|
|
9
|
+
taskId?: string | null;
|
|
10
|
+
openedAt?: string | null;
|
|
11
|
+
lastActiveAt?: string | null;
|
|
12
|
+
provider?: string | null;
|
|
13
|
+
actorLabel?: string | null;
|
|
14
|
+
workspaceHost?: string | null;
|
|
15
|
+
folderScope?: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface RuntimeActivityBuildInput {
|
|
18
|
+
cwd: string;
|
|
19
|
+
repo: RepoDetectionResult;
|
|
20
|
+
stableSubjectId?: string | undefined;
|
|
21
|
+
stableWorktreeId?: string | undefined;
|
|
22
|
+
changedFiles?: string[];
|
|
23
|
+
provider: string;
|
|
24
|
+
model: string;
|
|
25
|
+
serviceName: string;
|
|
26
|
+
serviceKind?: string | null;
|
|
27
|
+
status?: string | null;
|
|
28
|
+
startedAt: string;
|
|
29
|
+
endedAt?: string | null;
|
|
30
|
+
summary?: string | null;
|
|
31
|
+
clientName?: string | null;
|
|
32
|
+
commandLabel?: string | null;
|
|
33
|
+
includeServiceNameAsActor?: boolean | undefined;
|
|
34
|
+
actorLabels?: string[];
|
|
35
|
+
taskLabels?: string[];
|
|
36
|
+
activityThreads?: RuntimeActivityThreadInput[];
|
|
37
|
+
folderLabels?: string[];
|
|
38
|
+
fileLabels?: string[];
|
|
39
|
+
scopeFileLabels?: string[];
|
|
40
|
+
modelLabels?: string[];
|
|
41
|
+
promptTokens?: number | undefined;
|
|
42
|
+
completionTokens?: number | undefined;
|
|
43
|
+
cachedPromptTokens?: number | undefined;
|
|
44
|
+
reasoningTokens?: number | undefined;
|
|
45
|
+
totalTokens?: number | undefined;
|
|
46
|
+
latencyMs?: number | undefined;
|
|
47
|
+
resources?: RuntimeActivityResourcesInput;
|
|
48
|
+
}
|
|
49
|
+
export interface RuntimeActivityBuildResult {
|
|
50
|
+
metadata: Record<string, unknown>;
|
|
51
|
+
steps: Array<Record<string, unknown>>;
|
|
52
|
+
summary: string | null;
|
|
53
|
+
}
|
|
54
|
+
export declare function buildRuntimeActivityMetadata(input: RuntimeActivityBuildInput): RuntimeActivityBuildResult;
|
|
55
|
+
//# sourceMappingURL=runtime-activity.d.ts.map
|