ai-project-manage-cli 7.1.17 → 7.1.19
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.js +706 -317
- package/dist/webide-message-worker.js +164 -34
- package/package.json +1 -1
|
@@ -342,6 +342,36 @@ function requireRemoteWorkdir(workdir) {
|
|
|
342
342
|
import { execFile } from "child_process";
|
|
343
343
|
import { promisify } from "util";
|
|
344
344
|
var execFileAsync = promisify(execFile);
|
|
345
|
+
function toHttpsGitRemoteUrl(raw) {
|
|
346
|
+
let s = raw.trim();
|
|
347
|
+
if (!s) return null;
|
|
348
|
+
const sshScp = /^git@([^:]+):(.+)$/.exec(s);
|
|
349
|
+
if (sshScp) {
|
|
350
|
+
s = `https://${sshScp[1]}/${sshScp[2]}`;
|
|
351
|
+
} else {
|
|
352
|
+
const sshUri = /^ssh:\/\/(?:git@)?([^/]+)\/(.+)$/i.exec(s);
|
|
353
|
+
if (sshUri) {
|
|
354
|
+
s = `https://${sshUri[1]}/${sshUri[2]}`;
|
|
355
|
+
} else if (/^http:\/\//i.test(s)) {
|
|
356
|
+
s = `https://${s.slice("http://".length)}`;
|
|
357
|
+
} else if (!/^https:\/\//i.test(s)) {
|
|
358
|
+
const scpLike = /^([^/:]+):(.+)$/.exec(s);
|
|
359
|
+
if (scpLike && !scpLike[1].includes(".")) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
if (scpLike) {
|
|
363
|
+
s = `https://${scpLike[1]}/${scpLike[2]}`;
|
|
364
|
+
} else if (s.includes("/")) {
|
|
365
|
+
s = `https://${s}`;
|
|
366
|
+
} else {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
s = s.replace(/\/+$/, "");
|
|
372
|
+
if (!/^https:\/\//i.test(s)) return null;
|
|
373
|
+
return s;
|
|
374
|
+
}
|
|
345
375
|
async function tryReadGitOriginUrl(cwd) {
|
|
346
376
|
try {
|
|
347
377
|
const { stdout } = await execFileAsync(
|
|
@@ -355,6 +385,11 @@ async function tryReadGitOriginUrl(cwd) {
|
|
|
355
385
|
return null;
|
|
356
386
|
}
|
|
357
387
|
}
|
|
388
|
+
async function tryReadHttpsGitOriginUrl(cwd) {
|
|
389
|
+
const raw = await tryReadGitOriginUrl(cwd);
|
|
390
|
+
if (!raw) return null;
|
|
391
|
+
return toHttpsGitRemoteUrl(raw);
|
|
392
|
+
}
|
|
358
393
|
|
|
359
394
|
// src/git-utils.ts
|
|
360
395
|
import { execFile as execFile2 } from "child_process";
|
|
@@ -718,7 +753,7 @@ import {
|
|
|
718
753
|
statSync as statSync2,
|
|
719
754
|
writeFileSync as writeFileSync3
|
|
720
755
|
} from "fs";
|
|
721
|
-
import { basename as basename2, join as join3, relative, resolve as resolve3 } from "path";
|
|
756
|
+
import { basename as basename2, dirname as dirname2, join as join3, relative, resolve as resolve3 } from "path";
|
|
722
757
|
var WORKSPACE_REPOS_MANIFEST = "workspace-repos.json";
|
|
723
758
|
var WORKSPACE_REPOS_VERSION = 1;
|
|
724
759
|
var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([".apm", "node_modules"]);
|
|
@@ -743,6 +778,20 @@ function toRelativeRepoPath(workdir, repoAbs) {
|
|
|
743
778
|
const rel = relative(workdir, repoAbs).replace(/\\/g, "/");
|
|
744
779
|
return rel === "" ? "." : rel;
|
|
745
780
|
}
|
|
781
|
+
function normalizeRepoEntry(raw) {
|
|
782
|
+
if (!raw || typeof raw !== "object") return null;
|
|
783
|
+
const o = raw;
|
|
784
|
+
if (typeof o.path !== "string" || !o.path.trim()) return null;
|
|
785
|
+
const entry = {
|
|
786
|
+
path: o.path.trim().replace(/\\/g, "/")
|
|
787
|
+
};
|
|
788
|
+
if (typeof o.remoteUrl === "string" && o.remoteUrl.trim()) {
|
|
789
|
+
entry.remoteUrl = o.remoteUrl.trim();
|
|
790
|
+
} else if (o.remoteUrl === null) {
|
|
791
|
+
entry.remoteUrl = null;
|
|
792
|
+
}
|
|
793
|
+
return entry;
|
|
794
|
+
}
|
|
746
795
|
function readManifest(workdir) {
|
|
747
796
|
const path = toFsPath(manifestPath(workdir));
|
|
748
797
|
if (!existsSync2(path)) {
|
|
@@ -764,7 +813,17 @@ function readManifest(workdir) {
|
|
|
764
813
|
if (typeof raw.workdir !== "string" || !raw.workdir.trim()) {
|
|
765
814
|
return null;
|
|
766
815
|
}
|
|
767
|
-
|
|
816
|
+
const repos = raw.repos.map((item) => normalizeRepoEntry(item)).filter((item) => item != null);
|
|
817
|
+
if (repos.length === 0) {
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
return {
|
|
821
|
+
version: WORKSPACE_REPOS_VERSION,
|
|
822
|
+
kind: raw.kind,
|
|
823
|
+
workdir: raw.workdir,
|
|
824
|
+
repos,
|
|
825
|
+
scannedAt: typeof raw.scannedAt === "string" && raw.scannedAt.trim() ? raw.scannedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
826
|
+
};
|
|
768
827
|
} catch {
|
|
769
828
|
return null;
|
|
770
829
|
}
|
|
@@ -859,6 +918,31 @@ function loadWorkspaceReposCache(workdirInput) {
|
|
|
859
918
|
workdir
|
|
860
919
|
};
|
|
861
920
|
}
|
|
921
|
+
async function enrichWorkspaceReposRemoteUrls(manifest) {
|
|
922
|
+
let changed = false;
|
|
923
|
+
const repos = [];
|
|
924
|
+
for (const entry of manifest.repos) {
|
|
925
|
+
const abs = absoluteRepoPath(manifest.workdir, entry);
|
|
926
|
+
const remoteUrl = await tryReadHttpsGitOriginUrl(abs);
|
|
927
|
+
const next = {
|
|
928
|
+
path: entry.path,
|
|
929
|
+
remoteUrl: remoteUrl || null
|
|
930
|
+
};
|
|
931
|
+
if ((entry.remoteUrl ?? null) !== next.remoteUrl) {
|
|
932
|
+
changed = true;
|
|
933
|
+
}
|
|
934
|
+
repos.push(next);
|
|
935
|
+
}
|
|
936
|
+
const nextManifest = {
|
|
937
|
+
...manifest,
|
|
938
|
+
repos,
|
|
939
|
+
scannedAt: changed ? (/* @__PURE__ */ new Date()).toISOString() : manifest.scannedAt
|
|
940
|
+
};
|
|
941
|
+
if (changed) {
|
|
942
|
+
writeManifest(manifest.workdir, nextManifest);
|
|
943
|
+
}
|
|
944
|
+
return nextManifest;
|
|
945
|
+
}
|
|
862
946
|
function resolveWorkspaceRepos(workdirInput, options) {
|
|
863
947
|
const workdir = resolveWorkdirPath(workdirInput);
|
|
864
948
|
if (!options?.forceRescan) {
|
|
@@ -871,6 +955,15 @@ function resolveWorkspaceRepos(workdirInput, options) {
|
|
|
871
955
|
}
|
|
872
956
|
}
|
|
873
957
|
const scanned = scanWorkspaceRepos(workdir);
|
|
958
|
+
const previous = readManifest(workdir);
|
|
959
|
+
if (previous) {
|
|
960
|
+
const prevByPath = new Map(previous.repos.map((r) => [r.path, r]));
|
|
961
|
+
scanned.repos = scanned.repos.map((entry) => {
|
|
962
|
+
const old = prevByPath.get(entry.path);
|
|
963
|
+
if (!old?.remoteUrl) return entry;
|
|
964
|
+
return { ...entry, remoteUrl: old.remoteUrl };
|
|
965
|
+
});
|
|
966
|
+
}
|
|
874
967
|
writeManifest(workdir, scanned);
|
|
875
968
|
console.log(
|
|
876
969
|
`[apm] \u5DF2\u626B\u63CF\u5DE5\u4F5C\u533A\u4ED3\u5E93 kind=${scanned.kind} repos=${scanned.repos.map((r) => r.path).join(", ")} \u2192 .apm/${WORKSPACE_REPOS_MANIFEST}`
|
|
@@ -1329,7 +1422,7 @@ ${JSON.stringify(event, null, 2)}
|
|
|
1329
1422
|
|
|
1330
1423
|
// src/commands/connect/agent-session-registry.ts
|
|
1331
1424
|
import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1332
|
-
import { dirname as
|
|
1425
|
+
import { dirname as dirname3, resolve as resolve4 } from "node:path";
|
|
1333
1426
|
function registryPath(workdir, sessionId) {
|
|
1334
1427
|
return resolve4(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
|
|
1335
1428
|
}
|
|
@@ -1355,7 +1448,7 @@ function readRegistry(path) {
|
|
|
1355
1448
|
return {};
|
|
1356
1449
|
}
|
|
1357
1450
|
function writeRegistry(path, registry) {
|
|
1358
|
-
mkdirSync4(
|
|
1451
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
1359
1452
|
writeFileSync4(path, `${JSON.stringify(registry, null, 2)}
|
|
1360
1453
|
`, "utf8");
|
|
1361
1454
|
}
|
|
@@ -2279,7 +2372,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2279
2372
|
|
|
2280
2373
|
// src/commands/connect/webide-agent-registry.ts
|
|
2281
2374
|
import { existsSync as existsSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2282
|
-
import { dirname as
|
|
2375
|
+
import { dirname as dirname4, resolve as resolve5 } from "node:path";
|
|
2283
2376
|
function registryPath2(workdir, taskId) {
|
|
2284
2377
|
return resolve5(workdir, ".apm", "webide", taskId, "cursor-agent.json");
|
|
2285
2378
|
}
|
|
@@ -2300,7 +2393,7 @@ function readRegistry2(path) {
|
|
|
2300
2393
|
return {};
|
|
2301
2394
|
}
|
|
2302
2395
|
function writeRegistry2(path, registry) {
|
|
2303
|
-
mkdirSync6(
|
|
2396
|
+
mkdirSync6(dirname4(path), { recursive: true });
|
|
2304
2397
|
writeFileSync5(path, `${JSON.stringify(registry, null, 2)}
|
|
2305
2398
|
`, "utf8");
|
|
2306
2399
|
}
|
|
@@ -2625,7 +2718,24 @@ async function resolveRepositoryIdForSync(api, workdirPath) {
|
|
|
2625
2718
|
};
|
|
2626
2719
|
}
|
|
2627
2720
|
}
|
|
2628
|
-
async function
|
|
2721
|
+
async function writeDeploymentConfigContent(apmDir, content, configName) {
|
|
2722
|
+
let parsed;
|
|
2723
|
+
try {
|
|
2724
|
+
parsed = JSON.parse(content);
|
|
2725
|
+
} catch {
|
|
2726
|
+
console.warn(
|
|
2727
|
+
`[apm] \u8FDC\u7A0B\u90E8\u7F72\u914D\u7F6E\u300C${configName}\u300DJSON \u65E0\u6548\uFF08${TEMPLATE_HINT}\uFF09`
|
|
2728
|
+
);
|
|
2729
|
+
return false;
|
|
2730
|
+
}
|
|
2731
|
+
const apmConfigPath = toFsPath(join5(apmDir, "apm.config.json"));
|
|
2732
|
+
writeFileSync6(apmConfigPath, `${JSON.stringify(parsed, null, 2)}
|
|
2733
|
+
`, "utf8");
|
|
2734
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E: ${configName}`);
|
|
2735
|
+
console.log("[apm] \u5DF2\u5199\u5165 .apm/apm.config.json");
|
|
2736
|
+
return true;
|
|
2737
|
+
}
|
|
2738
|
+
async function syncRemoteDeploymentConfig(workdirPath, apmDir, options) {
|
|
2629
2739
|
const cfg = await tryReadApmConfig();
|
|
2630
2740
|
if (!cfg || !resolveApiKey(cfg)) {
|
|
2631
2741
|
console.log(
|
|
@@ -2635,10 +2745,14 @@ async function syncRemoteDeploymentConfig(workdirPath, apmDir) {
|
|
|
2635
2745
|
return { synced: false, repositoryId: null };
|
|
2636
2746
|
}
|
|
2637
2747
|
const api = createApmApiClient(cfg);
|
|
2638
|
-
const
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
)
|
|
2748
|
+
const preferredId = options?.repositoryId?.trim() || "";
|
|
2749
|
+
let repositoryId = preferredId || null;
|
|
2750
|
+
let diagnostic = null;
|
|
2751
|
+
if (!repositoryId) {
|
|
2752
|
+
const resolved = await resolveRepositoryIdForSync(api, workdirPath);
|
|
2753
|
+
repositoryId = resolved.repositoryId;
|
|
2754
|
+
diagnostic = resolved.diagnostic;
|
|
2755
|
+
}
|
|
2642
2756
|
if (!repositoryId) {
|
|
2643
2757
|
console.log(
|
|
2644
2758
|
`[apm] \u672A\u80FD\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E\uFF08${TEMPLATE_HINT}\uFF09\u3002
|
|
@@ -2655,21 +2769,15 @@ ${diagnostic ?? ""}
|
|
|
2655
2769
|
);
|
|
2656
2770
|
return { synced: false, repositoryId };
|
|
2657
2771
|
}
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2772
|
+
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2773
|
+
const wrote = await writeDeploymentConfigContent(
|
|
2774
|
+
targetApmDir,
|
|
2775
|
+
config.content,
|
|
2776
|
+
config.name
|
|
2777
|
+
);
|
|
2778
|
+
if (!wrote) {
|
|
2665
2779
|
return { synced: false, repositoryId };
|
|
2666
2780
|
}
|
|
2667
|
-
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2668
|
-
const apmConfigPath = toFsPath(join5(targetApmDir, "apm.config.json"));
|
|
2669
|
-
writeFileSync6(apmConfigPath, `${JSON.stringify(parsed, null, 2)}
|
|
2670
|
-
`, "utf8");
|
|
2671
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E: ${config.name}`);
|
|
2672
|
-
console.log("[apm] \u5DF2\u5199\u5165 .apm/apm.config.json");
|
|
2673
2781
|
return { synced: true, repositoryId, configName: config.name };
|
|
2674
2782
|
}
|
|
2675
2783
|
|
|
@@ -2682,7 +2790,7 @@ import {
|
|
|
2682
2790
|
writeFileSync as writeFileSync7
|
|
2683
2791
|
} from "fs";
|
|
2684
2792
|
import { createHash } from "crypto";
|
|
2685
|
-
import { dirname as
|
|
2793
|
+
import { dirname as dirname5, join as join6, relative as relative2, sep } from "path";
|
|
2686
2794
|
var MANIFEST_FILE = "manifest.json";
|
|
2687
2795
|
function normalizeProjectIdForPath(projectId) {
|
|
2688
2796
|
const id = projectId.trim();
|
|
@@ -2861,7 +2969,7 @@ ${diagnostic ?? ""}`);
|
|
|
2861
2969
|
const absPath = toFsPath(
|
|
2862
2970
|
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
2863
2971
|
);
|
|
2864
|
-
await ensureDirExists(
|
|
2972
|
+
await ensureDirExists(dirname5(absPath));
|
|
2865
2973
|
writeFileSync7(absPath, doc.content, "utf8");
|
|
2866
2974
|
downloaded += 1;
|
|
2867
2975
|
}
|
|
@@ -2987,15 +3095,18 @@ function markBranchDone(sessionId, workdir) {
|
|
|
2987
3095
|
}
|
|
2988
3096
|
|
|
2989
3097
|
// src/commands/connect/webide-pull-requests.ts
|
|
2990
|
-
var
|
|
3098
|
+
var PRE_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set([
|
|
2991
3099
|
"generate-cases",
|
|
2992
3100
|
"run-auto-test",
|
|
2993
|
-
//
|
|
2994
|
-
"deploy",
|
|
3101
|
+
// 验收合并前兜底确保 PR 存在
|
|
2995
3102
|
"accept"
|
|
2996
3103
|
]);
|
|
3104
|
+
var POST_AGENT_PR_ACTIONS = /* @__PURE__ */ new Set(["create-pr", "finish-manual-test"]);
|
|
2997
3105
|
function shouldEnsureWebIdePullRequests(action) {
|
|
2998
|
-
return
|
|
3106
|
+
return PRE_AGENT_PR_ACTIONS.has(action);
|
|
3107
|
+
}
|
|
3108
|
+
function shouldEnsureWebIdePullRequestsAfterAgent(action) {
|
|
3109
|
+
return POST_AGENT_PR_ACTIONS.has(action);
|
|
2999
3110
|
}
|
|
3000
3111
|
async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
3001
3112
|
const api = createApmApiClient(cfg);
|
|
@@ -3047,11 +3158,11 @@ async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
|
3047
3158
|
|
|
3048
3159
|
// src/version.ts
|
|
3049
3160
|
import { readFileSync as readFileSync8 } from "fs";
|
|
3050
|
-
import { dirname as
|
|
3161
|
+
import { dirname as dirname6, join as join8 } from "path";
|
|
3051
3162
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3052
3163
|
function readCliVersion() {
|
|
3053
3164
|
try {
|
|
3054
|
-
const dir =
|
|
3165
|
+
const dir = dirname6(fileURLToPath2(import.meta.url));
|
|
3055
3166
|
const pkgPath = join8(dir, "..", "package.json");
|
|
3056
3167
|
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
3057
3168
|
return pkg.version ?? "0.0.0";
|
|
@@ -3344,7 +3455,8 @@ var WEBIDE_CODE_CHANGE_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
3344
3455
|
"start-develop",
|
|
3345
3456
|
"fix-and-retest",
|
|
3346
3457
|
"report-defect",
|
|
3347
|
-
"
|
|
3458
|
+
"create-pr",
|
|
3459
|
+
"finish-manual-test"
|
|
3348
3460
|
]);
|
|
3349
3461
|
function shouldCommitAfterWebIdeMessage(action) {
|
|
3350
3462
|
return WEBIDE_CODE_CHANGE_ACTIONS.has(action);
|
|
@@ -3385,7 +3497,15 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3385
3497
|
await syncWorkspaceSkills(cfg, workdir);
|
|
3386
3498
|
markSkillsSyncedForCliVersion(workdir, cliVersion);
|
|
3387
3499
|
}
|
|
3388
|
-
resolveWorkspaceRepos(workdir);
|
|
3500
|
+
const workspaceRepos = resolveWorkspaceRepos(workdir);
|
|
3501
|
+
try {
|
|
3502
|
+
await enrichWorkspaceReposRemoteUrls(workspaceRepos);
|
|
3503
|
+
} catch (err) {
|
|
3504
|
+
console.warn(
|
|
3505
|
+
"[apm] \u8865\u9F50 workspace-repos remoteUrl \u5931\u8D25:",
|
|
3506
|
+
err instanceof Error ? err.message : err
|
|
3507
|
+
);
|
|
3508
|
+
}
|
|
3389
3509
|
if (shouldRunBranch(branchCacheKey, workdir)) {
|
|
3390
3510
|
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3391
3511
|
const branchResult = await runTaskBranch(taskId, { cwd: workdir });
|
|
@@ -3550,6 +3670,16 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3550
3670
|
`[apm] webide \u81EA\u52A8 push action=${msg.action} repos=${pushed}`
|
|
3551
3671
|
);
|
|
3552
3672
|
}
|
|
3673
|
+
if (shouldEnsureWebIdePullRequestsAfterAgent(msg.action)) {
|
|
3674
|
+
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3675
|
+
if (!shouldCommitAfterWebIdeMessage(msg.action)) {
|
|
3676
|
+
const pushed = await pushWorkspaceRepos(workdir);
|
|
3677
|
+
console.log(
|
|
3678
|
+
`[apm] webide create-pr \u524D push action=${msg.action} repos=${pushed}`
|
|
3679
|
+
);
|
|
3680
|
+
}
|
|
3681
|
+
await ensureWebIdePullRequests(cfg, taskId, workdir);
|
|
3682
|
+
}
|
|
3553
3683
|
if (projectId) {
|
|
3554
3684
|
try {
|
|
3555
3685
|
await syncProjectDocumentsPush(cfg, workdir, void 0, { projectId });
|