ai-project-manage-cli 7.1.36 → 7.1.38
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 +625 -762
- package/dist/webide-message-worker.js +417 -646
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1000,20 +1000,12 @@ import { Command } from "commander";
|
|
|
1000
1000
|
init_config();
|
|
1001
1001
|
|
|
1002
1002
|
// src/commands/init.ts
|
|
1003
|
-
import { join as
|
|
1004
|
-
import { readFileSync as
|
|
1003
|
+
import { join as join3 } from "path";
|
|
1004
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1005
1005
|
|
|
1006
1006
|
// src/command-utils.ts
|
|
1007
1007
|
init_config();
|
|
1008
|
-
import {
|
|
1009
|
-
copyFileSync,
|
|
1010
|
-
existsSync,
|
|
1011
|
-
mkdirSync as mkdirSync2,
|
|
1012
|
-
readFileSync as readFileSync2,
|
|
1013
|
-
readdirSync,
|
|
1014
|
-
statSync,
|
|
1015
|
-
writeFileSync as writeFileSync2
|
|
1016
|
-
} from "fs";
|
|
1008
|
+
import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
|
|
1017
1009
|
import { basename, dirname, extname, join as join2, resolve as resolve2 } from "path";
|
|
1018
1010
|
import { fileURLToPath } from "url";
|
|
1019
1011
|
|
|
@@ -1084,63 +1076,6 @@ function isWorkspaceApmInitialized(workdir) {
|
|
|
1084
1076
|
}
|
|
1085
1077
|
return readdirSync(fsApmDir).length > 0;
|
|
1086
1078
|
}
|
|
1087
|
-
var APM_GITIGNORE_PATTERNS = [
|
|
1088
|
-
/^\.apm\/?$/,
|
|
1089
|
-
/^\.apm\/\*\*$/,
|
|
1090
|
-
/^\*\*\/\.apm\/?$/,
|
|
1091
|
-
/^\*\*\/\.apm\/\*\*$/,
|
|
1092
|
-
/^\/\.apm\/?$/,
|
|
1093
|
-
/^\/\.apm\/\*\*$/
|
|
1094
|
-
];
|
|
1095
|
-
function normalizeGitignorePattern(line) {
|
|
1096
|
-
const trimmed = line.trim();
|
|
1097
|
-
if (!trimmed || trimmed.startsWith("#")) return "";
|
|
1098
|
-
if (trimmed.startsWith("!")) return "";
|
|
1099
|
-
const hashIndex = trimmed.indexOf("#");
|
|
1100
|
-
return (hashIndex >= 0 ? trimmed.slice(0, hashIndex) : trimmed).trim();
|
|
1101
|
-
}
|
|
1102
|
-
function gitignoreIgnoresApm(line) {
|
|
1103
|
-
const pattern = normalizeGitignorePattern(line);
|
|
1104
|
-
if (!pattern) return false;
|
|
1105
|
-
return APM_GITIGNORE_PATTERNS.some((re) => re.test(pattern));
|
|
1106
|
-
}
|
|
1107
|
-
var APM_GITIGNORE_LINE = "**/.apm/**";
|
|
1108
|
-
function ensureApmGitignoredInRepo(workdir) {
|
|
1109
|
-
const gitignorePath = join2(workdir, ".gitignore");
|
|
1110
|
-
const fsGitignorePath = toFsPath(gitignorePath);
|
|
1111
|
-
if (!existsSync(fsGitignorePath)) {
|
|
1112
|
-
writeFileSync2(fsGitignorePath, `${APM_GITIGNORE_LINE}
|
|
1113
|
-
`, "utf8");
|
|
1114
|
-
return true;
|
|
1115
|
-
}
|
|
1116
|
-
const content = readFileSync2(fsGitignorePath, "utf8");
|
|
1117
|
-
if (content.split(/\r?\n/).some(gitignoreIgnoresApm)) {
|
|
1118
|
-
return false;
|
|
1119
|
-
}
|
|
1120
|
-
const suffix = content.endsWith("\n") || content.length === 0 ? "" : "\n";
|
|
1121
|
-
writeFileSync2(
|
|
1122
|
-
fsGitignorePath,
|
|
1123
|
-
`${content}${suffix}${APM_GITIGNORE_LINE}
|
|
1124
|
-
`,
|
|
1125
|
-
"utf8"
|
|
1126
|
-
);
|
|
1127
|
-
return true;
|
|
1128
|
-
}
|
|
1129
|
-
function assertApmGitignoredInRepo(workdir) {
|
|
1130
|
-
const gitignorePath = join2(workdir, ".gitignore");
|
|
1131
|
-
const fsGitignorePath = toFsPath(gitignorePath);
|
|
1132
|
-
if (!existsSync(fsGitignorePath)) {
|
|
1133
|
-
throw new Error(
|
|
1134
|
-
`\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u7F3A\u5C11 .gitignore \u6587\u4EF6\uFF0C\u8BF7\u6DFB\u52A0\u5BF9 .apm \u7684\u5FFD\u7565\u89C4\u5219\uFF08\u4F8B\u5982 **/.apm/**\uFF09\uFF0C\u907F\u514D\u672C\u5730\u4F1A\u8BDD\u4E0E\u90E8\u7F72\u51ED\u636E\u88AB\u63D0\u4EA4\u3002`
|
|
1135
|
-
);
|
|
1136
|
-
}
|
|
1137
|
-
const content = readFileSync2(fsGitignorePath, "utf8");
|
|
1138
|
-
if (!content.split(/\r?\n/).some(gitignoreIgnoresApm)) {
|
|
1139
|
-
throw new Error(
|
|
1140
|
-
`\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u7684 .gitignore \u672A\u5FFD\u7565 .apm\uFF0C\u8BF7\u6DFB\u52A0 **/.apm/** \u6216 .apm/\uFF0C\u907F\u514D\u672C\u5730\u4F1A\u8BDD\u4E0E\u90E8\u7F72\u51ED\u636E\u88AB\u63D0\u4EA4\u3002`
|
|
1141
|
-
);
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
1079
|
var SESSIONS_SUBDIR = "sessions";
|
|
1145
1080
|
var SESSION_DOCS_SUBDIR = "docs";
|
|
1146
1081
|
var SESSION_ATTACHMENTS_SUBDIR = "attachments";
|
|
@@ -1199,19 +1134,10 @@ async function ensureDirExists(dir) {
|
|
|
1199
1134
|
async function ensureWorkspaceApmDirForInit(cwd = resolveWorkdirPath()) {
|
|
1200
1135
|
const dir = workspaceApmDir(cwd);
|
|
1201
1136
|
const fsDir = toFsPath(dir);
|
|
1202
|
-
if (
|
|
1203
|
-
mkdirSync2(fsDir, { recursive: true });
|
|
1204
|
-
return;
|
|
1205
|
-
}
|
|
1206
|
-
const st = statSync(fsDir);
|
|
1207
|
-
if (!st.isDirectory()) {
|
|
1137
|
+
if (existsSync(fsDir) && !statSync(fsDir).isDirectory()) {
|
|
1208
1138
|
throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${dir}`);
|
|
1209
1139
|
}
|
|
1210
|
-
|
|
1211
|
-
throw new Error(
|
|
1212
|
-
"[apm] .apm \u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\uFF0C\u8BF7\u5148\u5907\u4EFD\u3001\u6E05\u7A7A\u6216\u5220\u9664\u540E\u518D\u6267\u884C init"
|
|
1213
|
-
);
|
|
1214
|
-
}
|
|
1140
|
+
mkdirSync2(fsDir, { recursive: true });
|
|
1215
1141
|
}
|
|
1216
1142
|
var WORKSPACE_TEMPLATE_SUBDIRS = [
|
|
1217
1143
|
"sessions",
|
|
@@ -1287,9 +1213,51 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
|
|
|
1287
1213
|
assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
|
|
1288
1214
|
}
|
|
1289
1215
|
|
|
1216
|
+
// src/commands/init.ts
|
|
1217
|
+
async function initializeWorkspace(workdir, options) {
|
|
1218
|
+
await ensureWorkspaceApmDirForInit(workdir);
|
|
1219
|
+
const apmDir = workspaceApmDir(workdir);
|
|
1220
|
+
await copyTemplateFiles(apmDir, workdir);
|
|
1221
|
+
const trimmedName = options?.name?.trim();
|
|
1222
|
+
if (trimmedName) {
|
|
1223
|
+
const apmConfigPath = toFsPath(join3(apmDir, "apm.config.json"));
|
|
1224
|
+
const config = readFileSync2(apmConfigPath, "utf8");
|
|
1225
|
+
const configJson = JSON.parse(config);
|
|
1226
|
+
configJson.name = trimmedName;
|
|
1227
|
+
writeFileSync2(
|
|
1228
|
+
apmConfigPath,
|
|
1229
|
+
`${JSON.stringify(configJson, null, 2)}
|
|
1230
|
+
`,
|
|
1231
|
+
"utf8"
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
|
|
1235
|
+
return { didInit: true };
|
|
1236
|
+
}
|
|
1237
|
+
async function ensureWorkspaceInitialized(workdir, options) {
|
|
1238
|
+
if (isWorkspaceApmInitialized(workdir)) {
|
|
1239
|
+
return { didInit: false };
|
|
1240
|
+
}
|
|
1241
|
+
console.log(
|
|
1242
|
+
`[apm] \u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u672A\u68C0\u6D4B\u5230\u5DF2\u521D\u59CB\u5316\u7684 .apm\uFF0C\u6B63\u5728\u81EA\u52A8\u521D\u59CB\u5316\u2026`
|
|
1243
|
+
);
|
|
1244
|
+
return initializeWorkspace(workdir, options);
|
|
1245
|
+
}
|
|
1246
|
+
async function runInit(name) {
|
|
1247
|
+
const workdir = resolveWorkdirPath();
|
|
1248
|
+
await initializeWorkspace(workdir, { name });
|
|
1249
|
+
console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// src/commands/login.ts
|
|
1253
|
+
init_config();
|
|
1254
|
+
init_client();
|
|
1255
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1256
|
+
import { ApiError } from "listpage-http";
|
|
1257
|
+
|
|
1290
1258
|
// src/deployment-config-sync.ts
|
|
1291
1259
|
init_client();
|
|
1292
|
-
import { join as
|
|
1260
|
+
import { join as join5 } from "path";
|
|
1293
1261
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
1294
1262
|
|
|
1295
1263
|
// src/git-remote.ts
|
|
@@ -1438,46 +1406,6 @@ async function resolveDefaultRemoteBranch(cwd) {
|
|
|
1438
1406
|
"[apm] \u65E0\u6CD5\u786E\u5B9A\u8FDC\u7A0B\u9ED8\u8BA4\u5206\u652F\uFF08origin/HEAD\u3001main\u3001master \u5747\u4E0D\u53EF\u7528\uFF09"
|
|
1439
1407
|
);
|
|
1440
1408
|
}
|
|
1441
|
-
async function hasUpstream(cwd) {
|
|
1442
|
-
try {
|
|
1443
|
-
await execGit(cwd, ["rev-parse", "--abbrev-ref", "@{upstream}"], true);
|
|
1444
|
-
return true;
|
|
1445
|
-
} catch {
|
|
1446
|
-
return false;
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
async function pushCurrentBranch(cwd) {
|
|
1450
|
-
if (!await isGitRepo(cwd)) {
|
|
1451
|
-
return false;
|
|
1452
|
-
}
|
|
1453
|
-
const originUrl = await tryReadGitOriginUrl(cwd);
|
|
1454
|
-
if (!originUrl) {
|
|
1455
|
-
console.log("[apm] \u672A\u914D\u7F6E remote.origin\uFF0C\u8DF3\u8FC7 push");
|
|
1456
|
-
return false;
|
|
1457
|
-
}
|
|
1458
|
-
if (await hasUpstream(cwd)) {
|
|
1459
|
-
await execGit(cwd, ["push"]);
|
|
1460
|
-
} else {
|
|
1461
|
-
await execGit(cwd, ["push", "-u", "origin", "HEAD"]);
|
|
1462
|
-
}
|
|
1463
|
-
return true;
|
|
1464
|
-
}
|
|
1465
|
-
var GITIGNORE_COMMIT_MESSAGE = "chore(apm): ignore .apm directory";
|
|
1466
|
-
async function commitAndPushGitignore(workdir) {
|
|
1467
|
-
if (!await isGitRepo(workdir)) {
|
|
1468
|
-
console.log("[apm] \u5F53\u524D\u76EE\u5F55\u4E0D\u662F git \u4ED3\u5E93\uFF0C\u8BF7\u624B\u52A8\u63D0\u4EA4 .gitignore");
|
|
1469
|
-
return;
|
|
1470
|
-
}
|
|
1471
|
-
await execGit(workdir, ["add", "--", ".gitignore"]);
|
|
1472
|
-
await execGit(workdir, ["commit", "-m", GITIGNORE_COMMIT_MESSAGE]);
|
|
1473
|
-
console.log(`[apm] \u5DF2\u63D0\u4EA4 .gitignore: ${GITIGNORE_COMMIT_MESSAGE}`);
|
|
1474
|
-
const pushed = await pushCurrentBranch(workdir);
|
|
1475
|
-
if (pushed) {
|
|
1476
|
-
console.log("[apm] \u5DF2\u63A8\u9001 .gitignore");
|
|
1477
|
-
} else {
|
|
1478
|
-
console.log("[apm] \u672A\u914D\u7F6E remote.origin\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8 push .gitignore");
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
1409
|
|
|
1482
1410
|
// src/baseline-resolve.ts
|
|
1483
1411
|
function formatBaselineDiagnostic(workdirPath, baselineWorkdirPath, diagnostic) {
|
|
@@ -1574,11 +1502,11 @@ import {
|
|
|
1574
1502
|
statSync as statSync2,
|
|
1575
1503
|
writeFileSync as writeFileSync3
|
|
1576
1504
|
} from "fs";
|
|
1577
|
-
import { basename as basename2, dirname as dirname2, join as
|
|
1505
|
+
import { basename as basename2, dirname as dirname2, join as join4, relative, resolve as resolve3 } from "path";
|
|
1578
1506
|
var WORKSPACE_REPOS_MANIFEST = "workspace-repos.json";
|
|
1579
1507
|
var WORKSPACE_REPOS_VERSION = 1;
|
|
1580
1508
|
function manifestPath(workdir) {
|
|
1581
|
-
return
|
|
1509
|
+
return join4(workspaceApmDir(workdir), WORKSPACE_REPOS_MANIFEST);
|
|
1582
1510
|
}
|
|
1583
1511
|
function absoluteRepoPath(workdir, entry) {
|
|
1584
1512
|
if (entry.path === "." || entry.path === "") {
|
|
@@ -1793,7 +1721,7 @@ async function writeDeploymentConfigContent(apmDir, content, configName) {
|
|
|
1793
1721
|
);
|
|
1794
1722
|
return false;
|
|
1795
1723
|
}
|
|
1796
|
-
const apmConfigPath = toFsPath(
|
|
1724
|
+
const apmConfigPath = toFsPath(join5(apmDir, "apm.config.json"));
|
|
1797
1725
|
writeFileSync4(apmConfigPath, `${JSON.stringify(parsed, null, 2)}
|
|
1798
1726
|
`, "utf8");
|
|
1799
1727
|
console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E: ${configName}`);
|
|
@@ -1846,456 +1774,130 @@ ${diagnostic ?? ""}
|
|
|
1846
1774
|
return { synced: true, repositoryId, configName: config.name };
|
|
1847
1775
|
}
|
|
1848
1776
|
|
|
1849
|
-
// src/
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
writeFileSync as writeFileSync5
|
|
1858
|
-
} from "fs";
|
|
1859
|
-
import { createHash } from "crypto";
|
|
1860
|
-
import { dirname as dirname3, join as join5, relative as relative2, sep } from "path";
|
|
1861
|
-
var MANIFEST_FILE = "manifest.json";
|
|
1862
|
-
function normalizeProjectIdForPath(projectId) {
|
|
1863
|
-
const id = projectId.trim();
|
|
1864
|
-
if (!id) {
|
|
1865
|
-
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1777
|
+
// src/commands/login.ts
|
|
1778
|
+
async function runLogin(opts) {
|
|
1779
|
+
const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
1780
|
+
const apiKey = (opts.apiKey?.trim() || process.env.APM_API_KEY?.trim() || "").replace(/^Bearer\s+/i, "");
|
|
1781
|
+
if (!apiKey.startsWith("cm_")) {
|
|
1782
|
+
console.error("[apm] \u8BF7\u63D0\u4F9B\u6709\u6548\u7684 API Key\uFF08cm_ \u5F00\u5934\uFF09");
|
|
1783
|
+
console.error("[apm] \u7528\u6CD5: apm login --api-key cm_xxx [--server URL]");
|
|
1784
|
+
process.exit(1);
|
|
1866
1785
|
}
|
|
1867
|
-
|
|
1868
|
-
|
|
1786
|
+
const api = createApmApiClient({
|
|
1787
|
+
baseUrl,
|
|
1788
|
+
clientMachineId: "",
|
|
1789
|
+
apiKey
|
|
1790
|
+
});
|
|
1791
|
+
let data;
|
|
1792
|
+
try {
|
|
1793
|
+
data = await api.cli.me({});
|
|
1794
|
+
} catch (raw) {
|
|
1795
|
+
if (raw instanceof ApiError) {
|
|
1796
|
+
const err = raw;
|
|
1797
|
+
console.error(
|
|
1798
|
+
`[apm] API Key \u9A8C\u8BC1\u5931\u8D25 (${err.httpStatus ?? err.code}):`,
|
|
1799
|
+
err.message
|
|
1800
|
+
);
|
|
1801
|
+
process.exit(1);
|
|
1802
|
+
}
|
|
1803
|
+
console.error("[apm] \u8BF7\u6C42\u5931\u8D25:", raw instanceof Error ? raw.message : raw);
|
|
1804
|
+
process.exit(1);
|
|
1869
1805
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1806
|
+
const clientMachineId = data?.clientMachineId;
|
|
1807
|
+
if (!clientMachineId) {
|
|
1808
|
+
console.error("[apm] \u54CD\u5E94\u7F3A\u5C11 clientMachineId");
|
|
1809
|
+
process.exit(1);
|
|
1810
|
+
}
|
|
1811
|
+
const cfg = {
|
|
1812
|
+
baseUrl,
|
|
1813
|
+
clientMachineId,
|
|
1814
|
+
apiKey
|
|
1815
|
+
};
|
|
1816
|
+
await writeApmConfig(cfg);
|
|
1817
|
+
console.error(`[apm] \u5DF2\u4FDD\u5B58\u767B\u5F55\u4FE1\u606F: ${APM_CONFIG_PATH}`);
|
|
1818
|
+
console.log(
|
|
1819
|
+
JSON.stringify(
|
|
1820
|
+
{
|
|
1821
|
+
clientMachineId: cfg.clientMachineId,
|
|
1822
|
+
clientName: data.clientName,
|
|
1823
|
+
baseUrl: cfg.baseUrl
|
|
1824
|
+
},
|
|
1825
|
+
null,
|
|
1826
|
+
2
|
|
1827
|
+
)
|
|
1881
1828
|
);
|
|
1829
|
+
const workdir = resolveWorkdirPath();
|
|
1830
|
+
const apmDir = workspaceApmDir(workdir);
|
|
1831
|
+
if (existsSync3(apmDir)) {
|
|
1832
|
+
await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
1833
|
+
}
|
|
1882
1834
|
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1835
|
+
|
|
1836
|
+
// src/commands/branch.ts
|
|
1837
|
+
init_client();
|
|
1838
|
+
var SESSION_BRANCH_PREFIX = "feat/session-";
|
|
1839
|
+
function branchNameForSession(sessionId) {
|
|
1840
|
+
const id = sessionId.trim();
|
|
1841
|
+
if (!id) {
|
|
1842
|
+
throw new Error("[apm] \u4F1A\u8BDD ID \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1887
1843
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1844
|
+
if (/[\s/\\]/.test(id)) {
|
|
1845
|
+
throw new Error(
|
|
1846
|
+
"[apm] \u4F1A\u8BDD ID \u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u6216\u8DEF\u5F84\u5206\u9694\u7B26\uFF0C\u8BF7\u4F7F\u7528\u5B57\u6BCD\u3001\u6570\u5B57\u3001._- \u7B49"
|
|
1847
|
+
);
|
|
1891
1848
|
}
|
|
1892
|
-
return
|
|
1893
|
-
}
|
|
1894
|
-
function hashLocalFileContent(content) {
|
|
1895
|
-
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
1849
|
+
return `${SESSION_BRANCH_PREFIX}${id}`;
|
|
1896
1850
|
}
|
|
1897
|
-
function
|
|
1898
|
-
const
|
|
1899
|
-
|
|
1900
|
-
MANIFEST_FILE
|
|
1901
|
-
);
|
|
1902
|
-
if (!existsSync3(manifestPath3)) {
|
|
1851
|
+
function sessionIdFromBranchName(branch) {
|
|
1852
|
+
const name = branch.trim().replace(/^origin\//, "");
|
|
1853
|
+
if (!name.startsWith(SESSION_BRANCH_PREFIX)) {
|
|
1903
1854
|
return null;
|
|
1904
1855
|
}
|
|
1856
|
+
const sessionId = name.slice(SESSION_BRANCH_PREFIX.length).trim();
|
|
1857
|
+
return sessionId || null;
|
|
1858
|
+
}
|
|
1859
|
+
async function localBranchExists(cwd, branch) {
|
|
1905
1860
|
try {
|
|
1906
|
-
|
|
1907
|
-
|
|
1861
|
+
await execGit(
|
|
1862
|
+
cwd,
|
|
1863
|
+
["show-ref", "--verify", "--quiet", `refs/heads/${branch}`],
|
|
1864
|
+
true
|
|
1908
1865
|
);
|
|
1866
|
+
return true;
|
|
1909
1867
|
} catch {
|
|
1910
|
-
return
|
|
1868
|
+
return false;
|
|
1911
1869
|
}
|
|
1912
1870
|
}
|
|
1913
|
-
function
|
|
1914
|
-
|
|
1915
|
-
if (!
|
|
1916
|
-
return
|
|
1871
|
+
async function commitWorkingTreeIfDirty(cwd, message) {
|
|
1872
|
+
await ensureGitRepo(cwd);
|
|
1873
|
+
if (!await isWorkingTreeDirty(cwd)) {
|
|
1874
|
+
return false;
|
|
1917
1875
|
}
|
|
1918
|
-
const
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
walk(abs);
|
|
1924
|
-
continue;
|
|
1925
|
-
}
|
|
1926
|
-
if (entry.isFile() && entry.name === MANIFEST_FILE) {
|
|
1927
|
-
continue;
|
|
1928
|
-
}
|
|
1929
|
-
const rel = relative2(root, abs).split(sep).join("/");
|
|
1930
|
-
paths.push(rel);
|
|
1931
|
-
}
|
|
1932
|
-
};
|
|
1933
|
-
walk(root);
|
|
1934
|
-
return paths.sort();
|
|
1876
|
+
const commitMessage = message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${await getCurrentBranch(cwd)})`;
|
|
1877
|
+
await execGit(cwd, ["add", "-A"]);
|
|
1878
|
+
await execGit(cwd, ["commit", "-m", commitMessage]);
|
|
1879
|
+
console.log(`[apm] \u5DF2\u63D0\u4EA4\u5DE5\u4F5C\u533A\u53D8\u66F4: ${commitMessage}`);
|
|
1880
|
+
return true;
|
|
1935
1881
|
}
|
|
1936
|
-
function
|
|
1937
|
-
const
|
|
1938
|
-
|
|
1939
|
-
)
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
);
|
|
1943
|
-
const
|
|
1944
|
-
|
|
1945
|
-
if (
|
|
1946
|
-
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
return { download, deleteLocal };
|
|
1956
|
-
}
|
|
1957
|
-
async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
1958
|
-
const explicit = options?.projectId?.trim();
|
|
1959
|
-
if (explicit) {
|
|
1960
|
-
return { projectId: explicit, diagnostic: null };
|
|
1961
|
-
}
|
|
1962
|
-
try {
|
|
1963
|
-
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
1964
|
-
const projectId = baseline.projectId?.trim() || null;
|
|
1965
|
-
if (projectId) {
|
|
1966
|
-
return { projectId, diagnostic: null };
|
|
1967
|
-
}
|
|
1968
|
-
const projectIds = baseline.projectIds ?? [];
|
|
1969
|
-
if (projectIds.length === 0) {
|
|
1970
|
-
return {
|
|
1971
|
-
projectId: null,
|
|
1972
|
-
diagnostic: baseline.diagnostic?.message ?? `\u5DE5\u4F5C\u7A7A\u95F4\u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF08\u76EE\u5F55\uFF1A${workdirPath}\uFF09\u3002
|
|
1973
|
-
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
1974
|
-
};
|
|
1975
|
-
}
|
|
1976
|
-
return {
|
|
1977
|
-
projectId: null,
|
|
1978
|
-
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
1979
|
-
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
1980
|
-
};
|
|
1981
|
-
} catch (err) {
|
|
1982
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1983
|
-
return {
|
|
1984
|
-
projectId: null,
|
|
1985
|
-
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
1986
|
-
};
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
1990
|
-
const empty = {
|
|
1991
|
-
synced: false,
|
|
1992
|
-
projectId: null,
|
|
1993
|
-
downloaded: 0,
|
|
1994
|
-
deleted: 0
|
|
1995
|
-
};
|
|
1996
|
-
const cfg = await tryReadApmConfig();
|
|
1997
|
-
if (!cfg || !resolveApiKey(cfg)) {
|
|
1998
|
-
console.log(
|
|
1999
|
-
"[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
|
|
2000
|
-
);
|
|
2001
|
-
return empty;
|
|
2002
|
-
}
|
|
2003
|
-
const api = createApmApiClient(cfg);
|
|
2004
|
-
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
2005
|
-
api,
|
|
2006
|
-
workdirPath,
|
|
2007
|
-
options
|
|
2008
|
-
);
|
|
2009
|
-
if (!projectId) {
|
|
2010
|
-
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
2011
|
-
${diagnostic ?? ""}`);
|
|
2012
|
-
return empty;
|
|
2013
|
-
}
|
|
2014
|
-
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2015
|
-
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
2016
|
-
await ensureDirExists(docsDir);
|
|
2017
|
-
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
2018
|
-
{ projectId }
|
|
2019
|
-
);
|
|
2020
|
-
if (!remoteManifest) {
|
|
2021
|
-
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
2022
|
-
return { ...empty, projectId };
|
|
2023
|
-
}
|
|
2024
|
-
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
2025
|
-
const { download, deleteLocal } = diffManifestPaths(
|
|
2026
|
-
remoteManifest,
|
|
2027
|
-
localManifest
|
|
2028
|
-
);
|
|
2029
|
-
let downloaded = 0;
|
|
2030
|
-
if (download.length > 0) {
|
|
2031
|
-
const { list } = await api.cli.listProjectDocuments({
|
|
2032
|
-
projectId,
|
|
2033
|
-
paths: download.join(",")
|
|
2034
|
-
});
|
|
2035
|
-
for (const doc of list) {
|
|
2036
|
-
const absPath = toFsPath(
|
|
2037
|
-
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
2038
|
-
);
|
|
2039
|
-
await ensureDirExists(dirname3(absPath));
|
|
2040
|
-
writeFileSync5(absPath, doc.content, "utf8");
|
|
2041
|
-
downloaded += 1;
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
|
-
let deleted = 0;
|
|
2045
|
-
for (const path19 of deleteLocal) {
|
|
2046
|
-
const absPath = toFsPath(
|
|
2047
|
-
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
2048
|
-
);
|
|
2049
|
-
if (existsSync3(absPath)) {
|
|
2050
|
-
rmSync(absPath, { force: true });
|
|
2051
|
-
deleted += 1;
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2054
|
-
writeFileSync5(
|
|
2055
|
-
toFsPath(join5(docsDir, MANIFEST_FILE)),
|
|
2056
|
-
`${JSON.stringify(remoteManifest, null, 2)}
|
|
2057
|
-
`,
|
|
2058
|
-
"utf8"
|
|
2059
|
-
);
|
|
2060
|
-
console.log(
|
|
2061
|
-
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
2062
|
-
);
|
|
2063
|
-
return {
|
|
2064
|
-
synced: true,
|
|
2065
|
-
projectId,
|
|
2066
|
-
downloaded,
|
|
2067
|
-
deleted
|
|
2068
|
-
};
|
|
2069
|
-
}
|
|
2070
|
-
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
2071
|
-
const api = createApmApiClient(cfg);
|
|
2072
|
-
const { projectId } = await resolveProjectIdForSync(
|
|
2073
|
-
api,
|
|
2074
|
-
workdirPath,
|
|
2075
|
-
options
|
|
2076
|
-
);
|
|
2077
|
-
if (!projectId) {
|
|
2078
|
-
return 0;
|
|
2079
|
-
}
|
|
2080
|
-
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
2081
|
-
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
2082
|
-
if (localPaths.length === 0) {
|
|
2083
|
-
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2084
|
-
return 0;
|
|
2085
|
-
}
|
|
2086
|
-
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
2087
|
-
const remoteHashByPath = new Map(
|
|
2088
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2089
|
-
);
|
|
2090
|
-
const remoteDescriptionByPath = new Map(
|
|
2091
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
2092
|
-
);
|
|
2093
|
-
let synced = 0;
|
|
2094
|
-
for (const path19 of localPaths) {
|
|
2095
|
-
const absPath = toFsPath(
|
|
2096
|
-
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
2097
|
-
);
|
|
2098
|
-
const content = readFileSync4(absPath, "utf8");
|
|
2099
|
-
const contentHash = hashLocalFileContent(content);
|
|
2100
|
-
if (remoteHashByPath.get(path19) === contentHash) {
|
|
2101
|
-
continue;
|
|
2102
|
-
}
|
|
2103
|
-
await api.cli.upsertProjectDocument({
|
|
2104
|
-
projectId,
|
|
2105
|
-
path: path19,
|
|
2106
|
-
content,
|
|
2107
|
-
description: remoteDescriptionByPath.get(path19) ?? void 0
|
|
2108
|
-
});
|
|
2109
|
-
synced += 1;
|
|
2110
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path19}`);
|
|
2111
|
-
}
|
|
2112
|
-
if (synced === 0) {
|
|
2113
|
-
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2114
|
-
}
|
|
2115
|
-
return synced;
|
|
2116
|
-
}
|
|
2117
|
-
|
|
2118
|
-
// src/commands/init.ts
|
|
2119
|
-
async function ensureWorkspaceInitialized(workdir, options) {
|
|
2120
|
-
if (isWorkspaceApmInitialized(workdir)) {
|
|
2121
|
-
return { didInit: false };
|
|
2122
|
-
}
|
|
2123
|
-
console.log(
|
|
2124
|
-
`[apm] \u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u672A\u68C0\u6D4B\u5230\u5DF2\u521D\u59CB\u5316\u7684 .apm\uFF0C\u6B63\u5728\u81EA\u52A8\u521D\u59CB\u5316\u2026`
|
|
2125
|
-
);
|
|
2126
|
-
await ensureWorkspaceApmDirForInit(workdir);
|
|
2127
|
-
if (ensureApmGitignoredInRepo(workdir)) {
|
|
2128
|
-
console.log("[apm] \u5DF2\u5728 .gitignore \u4E2D\u6DFB\u52A0 **/.apm/**");
|
|
2129
|
-
await commitAndPushGitignore(workdir);
|
|
2130
|
-
}
|
|
2131
|
-
const apmDir = workspaceApmDir(workdir);
|
|
2132
|
-
await copyTemplateFiles(apmDir, workdir);
|
|
2133
|
-
const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
2134
|
-
await syncProjectDocumentsPull(workdir, apmDir);
|
|
2135
|
-
const trimmedName = options?.name?.trim();
|
|
2136
|
-
if (trimmedName) {
|
|
2137
|
-
const apmConfigPath = toFsPath(join6(apmDir, "apm.config.json"));
|
|
2138
|
-
const config = readFileSync5(apmConfigPath, "utf8");
|
|
2139
|
-
const configJson = JSON.parse(config);
|
|
2140
|
-
configJson.name = trimmedName;
|
|
2141
|
-
writeFileSync6(
|
|
2142
|
-
apmConfigPath,
|
|
2143
|
-
`${JSON.stringify(configJson, null, 2)}
|
|
2144
|
-
`,
|
|
2145
|
-
"utf8"
|
|
2146
|
-
);
|
|
2147
|
-
}
|
|
2148
|
-
console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
|
|
2149
|
-
return { didInit: true, syncResult };
|
|
2150
|
-
}
|
|
2151
|
-
async function runInit(name) {
|
|
2152
|
-
const workdir = resolveWorkdirPath();
|
|
2153
|
-
await ensureWorkspaceApmDirForInit(workdir);
|
|
2154
|
-
const { didInit, syncResult } = await ensureWorkspaceInitialized(workdir, {
|
|
2155
|
-
name
|
|
2156
|
-
});
|
|
2157
|
-
if (!didInit) {
|
|
2158
|
-
throw new Error(
|
|
2159
|
-
"[apm] .apm \u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\uFF0C\u8BF7\u5148\u5907\u4EFD\u3001\u6E05\u7A7A\u6216\u5220\u9664\u540E\u518D\u6267\u884C init"
|
|
2160
|
-
);
|
|
2161
|
-
}
|
|
2162
|
-
console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
|
|
2163
|
-
if (syncResult && !syncResult.synced) {
|
|
2164
|
-
console.log(
|
|
2165
|
-
"[apm] \u5F53\u524D .apm/apm.config.json \u4E3A\u6A21\u677F\u9ED8\u8BA4\u503C\uFF1B\u5B8C\u6210\u5E73\u53F0\u767B\u8BB0\u540E\u6267\u884C apm sync-deploy-config"
|
|
2166
|
-
);
|
|
2167
|
-
}
|
|
2168
|
-
console.log("[apm] \u8BF7\u5728\u5E73\u53F0\u300C\u63A5\u5165\u7BA1\u7406 \u2192 \u5DE5\u4F5C\u7A7A\u95F4\u300D\u767B\u8BB0\u4E0A\u8FF0\u76EE\u5F55\u8DEF\u5F84");
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
// src/commands/login.ts
|
|
2172
|
-
init_config();
|
|
2173
|
-
init_client();
|
|
2174
|
-
import { existsSync as existsSync4 } from "fs";
|
|
2175
|
-
import { ApiError } from "listpage-http";
|
|
2176
|
-
async function runLogin(opts) {
|
|
2177
|
-
const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
2178
|
-
const apiKey = (opts.apiKey?.trim() || process.env.APM_API_KEY?.trim() || "").replace(/^Bearer\s+/i, "");
|
|
2179
|
-
if (!apiKey.startsWith("cm_")) {
|
|
2180
|
-
console.error("[apm] \u8BF7\u63D0\u4F9B\u6709\u6548\u7684 API Key\uFF08cm_ \u5F00\u5934\uFF09");
|
|
2181
|
-
console.error("[apm] \u7528\u6CD5: apm login --api-key cm_xxx [--server URL]");
|
|
2182
|
-
process.exit(1);
|
|
2183
|
-
}
|
|
2184
|
-
const api = createApmApiClient({
|
|
2185
|
-
baseUrl,
|
|
2186
|
-
clientMachineId: "",
|
|
2187
|
-
apiKey
|
|
2188
|
-
});
|
|
2189
|
-
let data;
|
|
2190
|
-
try {
|
|
2191
|
-
data = await api.cli.me({});
|
|
2192
|
-
} catch (raw) {
|
|
2193
|
-
if (raw instanceof ApiError) {
|
|
2194
|
-
const err = raw;
|
|
2195
|
-
console.error(
|
|
2196
|
-
`[apm] API Key \u9A8C\u8BC1\u5931\u8D25 (${err.httpStatus ?? err.code}):`,
|
|
2197
|
-
err.message
|
|
2198
|
-
);
|
|
2199
|
-
process.exit(1);
|
|
2200
|
-
}
|
|
2201
|
-
console.error("[apm] \u8BF7\u6C42\u5931\u8D25:", raw instanceof Error ? raw.message : raw);
|
|
2202
|
-
process.exit(1);
|
|
2203
|
-
}
|
|
2204
|
-
const clientMachineId = data?.clientMachineId;
|
|
2205
|
-
if (!clientMachineId) {
|
|
2206
|
-
console.error("[apm] \u54CD\u5E94\u7F3A\u5C11 clientMachineId");
|
|
2207
|
-
process.exit(1);
|
|
2208
|
-
}
|
|
2209
|
-
const cfg = {
|
|
2210
|
-
baseUrl,
|
|
2211
|
-
clientMachineId,
|
|
2212
|
-
apiKey
|
|
2213
|
-
};
|
|
2214
|
-
await writeApmConfig(cfg);
|
|
2215
|
-
console.error(`[apm] \u5DF2\u4FDD\u5B58\u767B\u5F55\u4FE1\u606F: ${APM_CONFIG_PATH}`);
|
|
2216
|
-
console.log(
|
|
2217
|
-
JSON.stringify(
|
|
2218
|
-
{
|
|
2219
|
-
clientMachineId: cfg.clientMachineId,
|
|
2220
|
-
clientName: data.clientName,
|
|
2221
|
-
baseUrl: cfg.baseUrl
|
|
2222
|
-
},
|
|
2223
|
-
null,
|
|
2224
|
-
2
|
|
2225
|
-
)
|
|
2226
|
-
);
|
|
2227
|
-
const workdir = resolveWorkdirPath();
|
|
2228
|
-
const apmDir = workspaceApmDir(workdir);
|
|
2229
|
-
if (existsSync4(apmDir)) {
|
|
2230
|
-
await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
2231
|
-
}
|
|
2232
|
-
}
|
|
2233
|
-
|
|
2234
|
-
// src/commands/branch.ts
|
|
2235
|
-
init_client();
|
|
2236
|
-
var SESSION_BRANCH_PREFIX = "feat/session-";
|
|
2237
|
-
function branchNameForSession(sessionId) {
|
|
2238
|
-
const id = sessionId.trim();
|
|
2239
|
-
if (!id) {
|
|
2240
|
-
throw new Error("[apm] \u4F1A\u8BDD ID \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2241
|
-
}
|
|
2242
|
-
if (/[\s/\\]/.test(id)) {
|
|
2243
|
-
throw new Error(
|
|
2244
|
-
"[apm] \u4F1A\u8BDD ID \u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u6216\u8DEF\u5F84\u5206\u9694\u7B26\uFF0C\u8BF7\u4F7F\u7528\u5B57\u6BCD\u3001\u6570\u5B57\u3001._- \u7B49"
|
|
2245
|
-
);
|
|
2246
|
-
}
|
|
2247
|
-
return `${SESSION_BRANCH_PREFIX}${id}`;
|
|
2248
|
-
}
|
|
2249
|
-
function sessionIdFromBranchName(branch) {
|
|
2250
|
-
const name = branch.trim().replace(/^origin\//, "");
|
|
2251
|
-
if (!name.startsWith(SESSION_BRANCH_PREFIX)) {
|
|
2252
|
-
return null;
|
|
2253
|
-
}
|
|
2254
|
-
const sessionId = name.slice(SESSION_BRANCH_PREFIX.length).trim();
|
|
2255
|
-
return sessionId || null;
|
|
2256
|
-
}
|
|
2257
|
-
async function localBranchExists(cwd, branch) {
|
|
2258
|
-
try {
|
|
2259
|
-
await execGit(
|
|
2260
|
-
cwd,
|
|
2261
|
-
["show-ref", "--verify", "--quiet", `refs/heads/${branch}`],
|
|
2262
|
-
true
|
|
2263
|
-
);
|
|
2264
|
-
return true;
|
|
2265
|
-
} catch {
|
|
2266
|
-
return false;
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2269
|
-
async function commitWorkingTreeIfDirty(cwd, message) {
|
|
2270
|
-
await ensureGitRepo(cwd);
|
|
2271
|
-
if (!await isWorkingTreeDirty(cwd)) {
|
|
2272
|
-
return false;
|
|
2273
|
-
}
|
|
2274
|
-
const commitMessage = message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${await getCurrentBranch(cwd)})`;
|
|
2275
|
-
await execGit(cwd, ["add", "-A"]);
|
|
2276
|
-
await execGit(cwd, ["commit", "-m", commitMessage]);
|
|
2277
|
-
console.log(`[apm] \u5DF2\u63D0\u4EA4\u5DE5\u4F5C\u533A\u53D8\u66F4: ${commitMessage}`);
|
|
2278
|
-
return true;
|
|
2279
|
-
}
|
|
2280
|
-
async function ensureFeatureBranch(branch, baselineBranch, options) {
|
|
2281
|
-
const cwd = options.cwd ?? process.cwd();
|
|
2282
|
-
const gitRoot = await resolveGitRepoRoot(cwd);
|
|
2283
|
-
const commitMessage = options.message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${branch})`;
|
|
2284
|
-
await ensureGitRepo(gitRoot);
|
|
2285
|
-
await ensureRemoteBaselineBranch(gitRoot, baselineBranch);
|
|
2286
|
-
const current = await getCurrentBranch(gitRoot);
|
|
2287
|
-
const dirty = await isWorkingTreeDirty(gitRoot);
|
|
2288
|
-
if (dirty) {
|
|
2289
|
-
if (current === branch) {
|
|
2290
|
-
await commitWorkingTreeIfDirty(gitRoot, commitMessage);
|
|
2291
|
-
} else {
|
|
2292
|
-
await execGit(gitRoot, [
|
|
2293
|
-
"stash",
|
|
2294
|
-
"push",
|
|
2295
|
-
"-u",
|
|
2296
|
-
"-m",
|
|
2297
|
-
`apm: switch to ${branch}`
|
|
2298
|
-
]);
|
|
1882
|
+
async function ensureFeatureBranch(branch, baselineBranch, options) {
|
|
1883
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1884
|
+
const gitRoot = await resolveGitRepoRoot(cwd);
|
|
1885
|
+
const commitMessage = options.message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${branch})`;
|
|
1886
|
+
await ensureGitRepo(gitRoot);
|
|
1887
|
+
await ensureRemoteBaselineBranch(gitRoot, baselineBranch);
|
|
1888
|
+
const current = await getCurrentBranch(gitRoot);
|
|
1889
|
+
const dirty = await isWorkingTreeDirty(gitRoot);
|
|
1890
|
+
if (dirty) {
|
|
1891
|
+
if (current === branch) {
|
|
1892
|
+
await commitWorkingTreeIfDirty(gitRoot, commitMessage);
|
|
1893
|
+
} else {
|
|
1894
|
+
await execGit(gitRoot, [
|
|
1895
|
+
"stash",
|
|
1896
|
+
"push",
|
|
1897
|
+
"-u",
|
|
1898
|
+
"-m",
|
|
1899
|
+
`apm: switch to ${branch}`
|
|
1900
|
+
]);
|
|
2299
1901
|
}
|
|
2300
1902
|
}
|
|
2301
1903
|
const onTargetBranch = await getCurrentBranch(gitRoot) === branch;
|
|
@@ -2682,7 +2284,7 @@ async function runCleanBranches(options = {}) {
|
|
|
2682
2284
|
|
|
2683
2285
|
// src/commands/pull.ts
|
|
2684
2286
|
init_client();
|
|
2685
|
-
import { writeFileSync as
|
|
2287
|
+
import { writeFileSync as writeFileSync9 } from "fs";
|
|
2686
2288
|
import { join as join10 } from "path";
|
|
2687
2289
|
import { stringify as yamlStringify } from "yaml";
|
|
2688
2290
|
|
|
@@ -2716,9 +2318,9 @@ function formatSessionMessagesXml(sessionId, messages) {
|
|
|
2716
2318
|
}
|
|
2717
2319
|
|
|
2718
2320
|
// src/commands/sync-session-attachments.ts
|
|
2719
|
-
import { existsSync as
|
|
2720
|
-
import { join as
|
|
2721
|
-
var
|
|
2321
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
2322
|
+
import { join as join6 } from "path";
|
|
2323
|
+
var MANIFEST_FILE = ".sync-manifest.json";
|
|
2722
2324
|
async function downloadAttachment(cfg, attachmentId) {
|
|
2723
2325
|
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
2724
2326
|
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({
|
|
@@ -2733,13 +2335,13 @@ async function downloadAttachment(cfg, attachmentId) {
|
|
|
2733
2335
|
return Buffer.from(await res.arrayBuffer());
|
|
2734
2336
|
}
|
|
2735
2337
|
function loadManifest(dir) {
|
|
2736
|
-
const path19 =
|
|
2737
|
-
if (!
|
|
2338
|
+
const path19 = join6(dir, MANIFEST_FILE);
|
|
2339
|
+
if (!existsSync4(path19)) {
|
|
2738
2340
|
return { version: 1, attachments: {} };
|
|
2739
2341
|
}
|
|
2740
2342
|
try {
|
|
2741
2343
|
const parsed = JSON.parse(
|
|
2742
|
-
|
|
2344
|
+
readFileSync4(path19, "utf8")
|
|
2743
2345
|
);
|
|
2744
2346
|
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
2745
2347
|
return parsed;
|
|
@@ -2749,15 +2351,15 @@ function loadManifest(dir) {
|
|
|
2749
2351
|
return { version: 1, attachments: {} };
|
|
2750
2352
|
}
|
|
2751
2353
|
function saveManifest(dir, manifest) {
|
|
2752
|
-
|
|
2753
|
-
|
|
2354
|
+
writeFileSync5(
|
|
2355
|
+
join6(dir, MANIFEST_FILE),
|
|
2754
2356
|
`${JSON.stringify(manifest, null, 2)}
|
|
2755
2357
|
`,
|
|
2756
2358
|
"utf8"
|
|
2757
2359
|
);
|
|
2758
2360
|
}
|
|
2759
2361
|
function isAttachmentUpToDate(entry, item, dest) {
|
|
2760
|
-
if (!entry || !
|
|
2362
|
+
if (!entry || !existsSync4(dest)) return false;
|
|
2761
2363
|
if (entry.name !== item.name) return false;
|
|
2762
2364
|
const createdAt = item.createdAt ?? "";
|
|
2763
2365
|
return entry.createdAt === createdAt;
|
|
@@ -2772,7 +2374,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
2772
2374
|
const nextManifest = { version: 1, attachments: {} };
|
|
2773
2375
|
const names = [];
|
|
2774
2376
|
for (const item of attachments) {
|
|
2775
|
-
const dest =
|
|
2377
|
+
const dest = join6(dir, item.name);
|
|
2776
2378
|
const entry = manifest.attachments[item.id];
|
|
2777
2379
|
const createdAt = item.createdAt ?? "";
|
|
2778
2380
|
names.push(item.name);
|
|
@@ -2782,7 +2384,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
2782
2384
|
continue;
|
|
2783
2385
|
}
|
|
2784
2386
|
const buffer = await downloadAttachment(cfg, item.id);
|
|
2785
|
-
|
|
2387
|
+
writeFileSync5(dest, buffer);
|
|
2786
2388
|
nextManifest.attachments[item.id] = {
|
|
2787
2389
|
name: item.name,
|
|
2788
2390
|
createdAt
|
|
@@ -2793,7 +2395,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
2793
2395
|
return names;
|
|
2794
2396
|
}
|
|
2795
2397
|
async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
2796
|
-
const dir =
|
|
2398
|
+
const dir = join6(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
|
|
2797
2399
|
return syncAttachmentsToDirectory(
|
|
2798
2400
|
cfg,
|
|
2799
2401
|
attachments,
|
|
@@ -2804,46 +2406,46 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
|
2804
2406
|
|
|
2805
2407
|
// src/rules-sync.ts
|
|
2806
2408
|
init_client();
|
|
2807
|
-
import { basename as basename3, extname as extname2, join as
|
|
2808
|
-
import { existsSync as
|
|
2409
|
+
import { basename as basename3, extname as extname2, join as join8 } from "path";
|
|
2410
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
2809
2411
|
|
|
2810
2412
|
// src/skills-sync.ts
|
|
2811
2413
|
import {
|
|
2812
2414
|
copyFileSync as copyFileSync2,
|
|
2813
2415
|
cpSync,
|
|
2814
|
-
existsSync as
|
|
2416
|
+
existsSync as existsSync5,
|
|
2815
2417
|
mkdirSync as mkdirSync4,
|
|
2816
|
-
readdirSync as
|
|
2817
|
-
rmSync
|
|
2418
|
+
readdirSync as readdirSync3,
|
|
2419
|
+
rmSync,
|
|
2818
2420
|
statSync as statSync3,
|
|
2819
|
-
writeFileSync as
|
|
2421
|
+
writeFileSync as writeFileSync6
|
|
2820
2422
|
} from "fs";
|
|
2821
|
-
import { join as
|
|
2822
|
-
var AGENTS_TEMPLATE_PATH =
|
|
2823
|
-
var BASE_SKILLS_TEMPLATE_DIR =
|
|
2824
|
-
var BASE_RULES_TEMPLATE_DIR =
|
|
2423
|
+
import { join as join7 } from "path";
|
|
2424
|
+
var AGENTS_TEMPLATE_PATH = join7(CLI_TEMPLATE_DIR, "AGENTS.md");
|
|
2425
|
+
var BASE_SKILLS_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "skills");
|
|
2426
|
+
var BASE_RULES_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "rules");
|
|
2825
2427
|
function sanitizeSkillDirName(name) {
|
|
2826
2428
|
const trimmed = name.trim();
|
|
2827
2429
|
if (!trimmed) return "skill";
|
|
2828
2430
|
return trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
2829
2431
|
}
|
|
2830
2432
|
function listBaseSkillDirNames() {
|
|
2831
|
-
if (!
|
|
2832
|
-
return
|
|
2833
|
-
const path19 =
|
|
2433
|
+
if (!existsSync5(BASE_SKILLS_TEMPLATE_DIR)) return [];
|
|
2434
|
+
return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
|
|
2435
|
+
const path19 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
2834
2436
|
return statSync3(path19).isDirectory();
|
|
2835
2437
|
});
|
|
2836
2438
|
}
|
|
2837
2439
|
function syncAgentsGuide(apmDir) {
|
|
2838
|
-
if (!
|
|
2440
|
+
if (!existsSync5(AGENTS_TEMPLATE_PATH)) return false;
|
|
2839
2441
|
mkdirSync4(apmDir, { recursive: true });
|
|
2840
|
-
copyFileSync2(AGENTS_TEMPLATE_PATH,
|
|
2442
|
+
copyFileSync2(AGENTS_TEMPLATE_PATH, join7(apmDir, "AGENTS.md"));
|
|
2841
2443
|
return true;
|
|
2842
2444
|
}
|
|
2843
2445
|
function listBaseRuleFileNames() {
|
|
2844
|
-
if (!
|
|
2845
|
-
return
|
|
2846
|
-
const path19 =
|
|
2446
|
+
if (!existsSync5(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
2447
|
+
return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
2448
|
+
const path19 = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
2847
2449
|
return statSync3(path19).isFile();
|
|
2848
2450
|
});
|
|
2849
2451
|
}
|
|
@@ -2851,144 +2453,413 @@ function syncBaseRules(rulesDir) {
|
|
|
2851
2453
|
mkdirSync4(rulesDir, { recursive: true });
|
|
2852
2454
|
const names = listBaseRuleFileNames();
|
|
2853
2455
|
for (const name of names) {
|
|
2854
|
-
const src =
|
|
2855
|
-
const dest =
|
|
2456
|
+
const src = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
2457
|
+
const dest = join7(rulesDir, name);
|
|
2856
2458
|
copyFileSync2(src, dest);
|
|
2857
2459
|
}
|
|
2858
|
-
return names;
|
|
2460
|
+
return names;
|
|
2461
|
+
}
|
|
2462
|
+
function syncBaseSkills(skillsDir) {
|
|
2463
|
+
mkdirSync4(skillsDir, { recursive: true });
|
|
2464
|
+
const names = listBaseSkillDirNames();
|
|
2465
|
+
for (const name of names) {
|
|
2466
|
+
const src = join7(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
2467
|
+
const dest = join7(skillsDir, name);
|
|
2468
|
+
cpSync(src, dest, { recursive: true, force: true });
|
|
2469
|
+
}
|
|
2470
|
+
return names;
|
|
2471
|
+
}
|
|
2472
|
+
function syncSupplementarySkills(skillsDir, list) {
|
|
2473
|
+
const baseNames = new Set(listBaseSkillDirNames());
|
|
2474
|
+
const apiDirNames = /* @__PURE__ */ new Set();
|
|
2475
|
+
const written = [];
|
|
2476
|
+
const skipped = [];
|
|
2477
|
+
for (const skill of list) {
|
|
2478
|
+
const dirName = sanitizeSkillDirName(skill.name);
|
|
2479
|
+
apiDirNames.add(dirName);
|
|
2480
|
+
if (baseNames.has(dirName)) {
|
|
2481
|
+
skipped.push(dirName);
|
|
2482
|
+
continue;
|
|
2483
|
+
}
|
|
2484
|
+
const skillDir = join7(skillsDir, dirName);
|
|
2485
|
+
mkdirSync4(skillDir, { recursive: true });
|
|
2486
|
+
writeFileSync6(join7(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
|
|
2487
|
+
written.push(dirName);
|
|
2488
|
+
}
|
|
2489
|
+
const removed = [];
|
|
2490
|
+
if (!existsSync5(skillsDir)) return { written, skipped, removed };
|
|
2491
|
+
for (const entry of readdirSync3(skillsDir)) {
|
|
2492
|
+
const full = join7(skillsDir, entry);
|
|
2493
|
+
if (!statSync3(full).isDirectory()) continue;
|
|
2494
|
+
if (baseNames.has(entry)) continue;
|
|
2495
|
+
if (apiDirNames.has(entry)) continue;
|
|
2496
|
+
rmSync(full, { recursive: true, force: true });
|
|
2497
|
+
removed.push(entry);
|
|
2498
|
+
}
|
|
2499
|
+
return { written, skipped, removed };
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// src/rules-sync.ts
|
|
2503
|
+
var MANIFEST_FILE2 = ".rules-sync-manifest.json";
|
|
2504
|
+
function ruleLocalFileName(ruleName) {
|
|
2505
|
+
const trimmed = ruleName.trim();
|
|
2506
|
+
if (!trimmed) return "rule.md";
|
|
2507
|
+
const sanitized = trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
2508
|
+
if (extname2(sanitized).toLowerCase() === ".md") return sanitized;
|
|
2509
|
+
return `${sanitized}.md`;
|
|
2510
|
+
}
|
|
2511
|
+
function loadManifest2(rulesDir) {
|
|
2512
|
+
const path19 = join8(rulesDir, MANIFEST_FILE2);
|
|
2513
|
+
if (!existsSync6(toFsPath(path19))) {
|
|
2514
|
+
return { version: 1, rules: {} };
|
|
2515
|
+
}
|
|
2516
|
+
try {
|
|
2517
|
+
const parsed = JSON.parse(
|
|
2518
|
+
readFileSync5(toFsPath(path19), "utf8")
|
|
2519
|
+
);
|
|
2520
|
+
if (parsed?.version === 1 && parsed.rules && typeof parsed.rules === "object") {
|
|
2521
|
+
return parsed;
|
|
2522
|
+
}
|
|
2523
|
+
} catch {
|
|
2524
|
+
}
|
|
2525
|
+
return { version: 1, rules: {} };
|
|
2526
|
+
}
|
|
2527
|
+
function saveManifest2(rulesDir, manifest) {
|
|
2528
|
+
writeFileSync7(
|
|
2529
|
+
toFsPath(join8(rulesDir, MANIFEST_FILE2)),
|
|
2530
|
+
`${JSON.stringify(manifest, null, 2)}
|
|
2531
|
+
`,
|
|
2532
|
+
"utf8"
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
2535
|
+
function isBaseRuleFileName(fileName) {
|
|
2536
|
+
return listBaseRuleFileNames().includes(basename3(fileName));
|
|
2537
|
+
}
|
|
2538
|
+
function isRuleUpToDate(entry, rule, dest) {
|
|
2539
|
+
if (!entry || !existsSync6(toFsPath(dest))) return false;
|
|
2540
|
+
if (entry.fileName !== ruleLocalFileName(rule.name)) return false;
|
|
2541
|
+
const updatedAt = rule.updatedAt ?? "";
|
|
2542
|
+
if (entry.updatedAt !== updatedAt) return false;
|
|
2543
|
+
const localContent = readFileSync5(toFsPath(dest), "utf8");
|
|
2544
|
+
return localContent === (rule.content ?? "");
|
|
2545
|
+
}
|
|
2546
|
+
async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
2547
|
+
const api = createApmApiClient(cfg);
|
|
2548
|
+
const baseline = await resolveBranchBaseline(api, sessionId, workdirPath);
|
|
2549
|
+
const repositoryId = baseline.repositoryId;
|
|
2550
|
+
const rulesDir = join8(apmRoot ?? workspaceApmDir(workdirPath), "rules");
|
|
2551
|
+
await ensureDirExists(rulesDir);
|
|
2552
|
+
if (!repositoryId) {
|
|
2553
|
+
console.log(
|
|
2554
|
+
`[apm] \u672A\u5339\u914D\u5230\u7ED1\u5B9A\u4ED3\u5E93\u7684\u5DE5\u4F5C\u7A7A\u95F4\uFF0C\u8DF3\u8FC7\u5E73\u53F0\u89C4\u5219\u540C\u6B65\uFF08\u8DEF\u5F84\uFF1A${workdirPath}\uFF09`
|
|
2555
|
+
);
|
|
2556
|
+
return { written: [], skipped: [], removed: [], repositoryId: null };
|
|
2557
|
+
}
|
|
2558
|
+
const { list } = await api.cli.listRules({ repositoryId });
|
|
2559
|
+
const manifest = loadManifest2(rulesDir);
|
|
2560
|
+
const nextManifest = { version: 1, rules: {} };
|
|
2561
|
+
const remoteIds = /* @__PURE__ */ new Set();
|
|
2562
|
+
const written = [];
|
|
2563
|
+
const skipped = [];
|
|
2564
|
+
for (const rule of list) {
|
|
2565
|
+
remoteIds.add(rule.id);
|
|
2566
|
+
const fileName = ruleLocalFileName(rule.name);
|
|
2567
|
+
const dest = join8(rulesDir, fileName);
|
|
2568
|
+
const entry = manifest.rules[rule.id];
|
|
2569
|
+
const updatedAt = rule.updatedAt ?? "";
|
|
2570
|
+
if (isRuleUpToDate(entry, rule, dest)) {
|
|
2571
|
+
nextManifest.rules[rule.id] = entry;
|
|
2572
|
+
skipped.push(fileName);
|
|
2573
|
+
console.log(`[apm] \u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: rules/${fileName}`);
|
|
2574
|
+
continue;
|
|
2575
|
+
}
|
|
2576
|
+
writeFileSync7(toFsPath(dest), rule.content ?? "", "utf8");
|
|
2577
|
+
nextManifest.rules[rule.id] = { fileName, updatedAt };
|
|
2578
|
+
written.push(fileName);
|
|
2579
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
|
|
2580
|
+
}
|
|
2581
|
+
const removed = [];
|
|
2582
|
+
for (const [ruleId, entry] of Object.entries(manifest.rules)) {
|
|
2583
|
+
if (remoteIds.has(ruleId)) continue;
|
|
2584
|
+
if (isBaseRuleFileName(entry.fileName)) continue;
|
|
2585
|
+
const dest = join8(rulesDir, entry.fileName);
|
|
2586
|
+
if (existsSync6(toFsPath(dest))) {
|
|
2587
|
+
rmSync2(toFsPath(dest), { force: true });
|
|
2588
|
+
}
|
|
2589
|
+
removed.push(entry.fileName);
|
|
2590
|
+
console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u5E73\u53F0\u89C4\u5219: rules/${entry.fileName}`);
|
|
2591
|
+
}
|
|
2592
|
+
saveManifest2(rulesDir, nextManifest);
|
|
2593
|
+
return { written, skipped, removed, repositoryId };
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
// src/project-documents-sync.ts
|
|
2597
|
+
init_client();
|
|
2598
|
+
init_config();
|
|
2599
|
+
import {
|
|
2600
|
+
existsSync as existsSync7,
|
|
2601
|
+
readdirSync as readdirSync4,
|
|
2602
|
+
readFileSync as readFileSync6,
|
|
2603
|
+
rmSync as rmSync3,
|
|
2604
|
+
writeFileSync as writeFileSync8
|
|
2605
|
+
} from "fs";
|
|
2606
|
+
import { createHash } from "crypto";
|
|
2607
|
+
import { dirname as dirname3, join as join9, relative as relative2, sep } from "path";
|
|
2608
|
+
var MANIFEST_FILE3 = "manifest.json";
|
|
2609
|
+
function normalizeProjectIdForPath(projectId) {
|
|
2610
|
+
const id = projectId.trim();
|
|
2611
|
+
if (!id) {
|
|
2612
|
+
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2613
|
+
}
|
|
2614
|
+
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
2615
|
+
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
2616
|
+
}
|
|
2617
|
+
return id;
|
|
2618
|
+
}
|
|
2619
|
+
function projectDocumentsDir(apmRoot, projectId) {
|
|
2620
|
+
const id = normalizeProjectIdForPath(projectId);
|
|
2621
|
+
return join9(apmRoot ?? workspaceApmDir(), "project", id);
|
|
2622
|
+
}
|
|
2623
|
+
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
2624
|
+
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
2625
|
+
return join9(
|
|
2626
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2627
|
+
...normalized.split("/")
|
|
2628
|
+
);
|
|
2629
|
+
}
|
|
2630
|
+
function normalizeLocalDocumentPath(path19) {
|
|
2631
|
+
const trimmed = path19.trim().replace(/\\/g, "/");
|
|
2632
|
+
if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
|
|
2633
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path19}`);
|
|
2634
|
+
}
|
|
2635
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
2636
|
+
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
2637
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path19}`);
|
|
2638
|
+
}
|
|
2639
|
+
return segments.join("/");
|
|
2640
|
+
}
|
|
2641
|
+
function hashLocalFileContent(content) {
|
|
2642
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
2643
|
+
}
|
|
2644
|
+
function readLocalManifest(apmRoot, projectId) {
|
|
2645
|
+
const manifestPath3 = join9(
|
|
2646
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2647
|
+
MANIFEST_FILE3
|
|
2648
|
+
);
|
|
2649
|
+
if (!existsSync7(manifestPath3)) {
|
|
2650
|
+
return null;
|
|
2651
|
+
}
|
|
2652
|
+
try {
|
|
2653
|
+
return JSON.parse(
|
|
2654
|
+
readFileSync6(manifestPath3, "utf8")
|
|
2655
|
+
);
|
|
2656
|
+
} catch {
|
|
2657
|
+
return null;
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
2661
|
+
const root = projectDocumentsDir(apmRoot, projectId);
|
|
2662
|
+
if (!existsSync7(root)) {
|
|
2663
|
+
return [];
|
|
2664
|
+
}
|
|
2665
|
+
const paths = [];
|
|
2666
|
+
const walk = (dir) => {
|
|
2667
|
+
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
2668
|
+
const abs = join9(dir, entry.name);
|
|
2669
|
+
if (entry.isDirectory()) {
|
|
2670
|
+
walk(abs);
|
|
2671
|
+
continue;
|
|
2672
|
+
}
|
|
2673
|
+
if (entry.isFile() && entry.name === MANIFEST_FILE3) {
|
|
2674
|
+
continue;
|
|
2675
|
+
}
|
|
2676
|
+
const rel = relative2(root, abs).split(sep).join("/");
|
|
2677
|
+
paths.push(rel);
|
|
2678
|
+
}
|
|
2679
|
+
};
|
|
2680
|
+
walk(root);
|
|
2681
|
+
return paths.sort();
|
|
2682
|
+
}
|
|
2683
|
+
function diffManifestPaths(remote, local) {
|
|
2684
|
+
const remoteMap = new Map(
|
|
2685
|
+
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2686
|
+
);
|
|
2687
|
+
const localMap = new Map(
|
|
2688
|
+
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2689
|
+
);
|
|
2690
|
+
const download = [];
|
|
2691
|
+
for (const [path19, hash] of remoteMap) {
|
|
2692
|
+
if (localMap.get(path19) !== hash) {
|
|
2693
|
+
download.push(path19);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
const deleteLocal = [];
|
|
2697
|
+
for (const path19 of localMap.keys()) {
|
|
2698
|
+
if (!remoteMap.has(path19)) {
|
|
2699
|
+
deleteLocal.push(path19);
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
return { download, deleteLocal };
|
|
2859
2703
|
}
|
|
2860
|
-
function
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
const src = join8(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
2865
|
-
const dest = join8(skillsDir, name);
|
|
2866
|
-
cpSync(src, dest, { recursive: true, force: true });
|
|
2704
|
+
async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
2705
|
+
const explicit = options?.projectId?.trim();
|
|
2706
|
+
if (explicit) {
|
|
2707
|
+
return { projectId: explicit, diagnostic: null };
|
|
2867
2708
|
}
|
|
2868
|
-
|
|
2869
|
-
}
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
const written = [];
|
|
2874
|
-
const skipped = [];
|
|
2875
|
-
for (const skill of list) {
|
|
2876
|
-
const dirName = sanitizeSkillDirName(skill.name);
|
|
2877
|
-
apiDirNames.add(dirName);
|
|
2878
|
-
if (baseNames.has(dirName)) {
|
|
2879
|
-
skipped.push(dirName);
|
|
2880
|
-
continue;
|
|
2709
|
+
try {
|
|
2710
|
+
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
2711
|
+
const projectId = baseline.projectId?.trim() || null;
|
|
2712
|
+
if (projectId) {
|
|
2713
|
+
return { projectId, diagnostic: null };
|
|
2881
2714
|
}
|
|
2882
|
-
const
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2715
|
+
const projectIds = baseline.projectIds ?? [];
|
|
2716
|
+
if (projectIds.length === 0) {
|
|
2717
|
+
return {
|
|
2718
|
+
projectId: null,
|
|
2719
|
+
diagnostic: baseline.diagnostic?.message ?? `\u5DE5\u4F5C\u7A7A\u95F4\u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF08\u76EE\u5F55\uFF1A${workdirPath}\uFF09\u3002
|
|
2720
|
+
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
2721
|
+
};
|
|
2722
|
+
}
|
|
2723
|
+
return {
|
|
2724
|
+
projectId: null,
|
|
2725
|
+
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
2726
|
+
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
2727
|
+
};
|
|
2728
|
+
} catch (err) {
|
|
2729
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
2730
|
+
return {
|
|
2731
|
+
projectId: null,
|
|
2732
|
+
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
2733
|
+
};
|
|
2896
2734
|
}
|
|
2897
|
-
return { written, skipped, removed };
|
|
2898
|
-
}
|
|
2899
|
-
|
|
2900
|
-
// src/rules-sync.ts
|
|
2901
|
-
var MANIFEST_FILE3 = ".rules-sync-manifest.json";
|
|
2902
|
-
function ruleLocalFileName(ruleName) {
|
|
2903
|
-
const trimmed = ruleName.trim();
|
|
2904
|
-
if (!trimmed) return "rule.md";
|
|
2905
|
-
const sanitized = trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
2906
|
-
if (extname2(sanitized).toLowerCase() === ".md") return sanitized;
|
|
2907
|
-
return `${sanitized}.md`;
|
|
2908
2735
|
}
|
|
2909
|
-
function
|
|
2910
|
-
const
|
|
2911
|
-
|
|
2912
|
-
|
|
2736
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
2737
|
+
const empty = {
|
|
2738
|
+
synced: false,
|
|
2739
|
+
projectId: null,
|
|
2740
|
+
downloaded: 0,
|
|
2741
|
+
deleted: 0
|
|
2742
|
+
};
|
|
2743
|
+
const cfg = await tryReadApmConfig();
|
|
2744
|
+
if (!cfg || !resolveApiKey(cfg)) {
|
|
2745
|
+
console.log(
|
|
2746
|
+
"[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
|
|
2747
|
+
);
|
|
2748
|
+
return empty;
|
|
2913
2749
|
}
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2750
|
+
const api = createApmApiClient(cfg);
|
|
2751
|
+
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
2752
|
+
api,
|
|
2753
|
+
workdirPath,
|
|
2754
|
+
options
|
|
2755
|
+
);
|
|
2756
|
+
if (!projectId) {
|
|
2757
|
+
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
2758
|
+
${diagnostic ?? ""}`);
|
|
2759
|
+
return empty;
|
|
2760
|
+
}
|
|
2761
|
+
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2762
|
+
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
2763
|
+
await ensureDirExists(docsDir);
|
|
2764
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
2765
|
+
{ projectId }
|
|
2766
|
+
);
|
|
2767
|
+
if (!remoteManifest) {
|
|
2768
|
+
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
2769
|
+
return { ...empty, projectId };
|
|
2770
|
+
}
|
|
2771
|
+
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
2772
|
+
const { download, deleteLocal } = diffManifestPaths(
|
|
2773
|
+
remoteManifest,
|
|
2774
|
+
localManifest
|
|
2775
|
+
);
|
|
2776
|
+
let downloaded = 0;
|
|
2777
|
+
if (download.length > 0) {
|
|
2778
|
+
const { list } = await api.cli.listProjectDocuments({
|
|
2779
|
+
projectId,
|
|
2780
|
+
paths: download.join(",")
|
|
2781
|
+
});
|
|
2782
|
+
for (const doc of list) {
|
|
2783
|
+
const absPath = toFsPath(
|
|
2784
|
+
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
2785
|
+
);
|
|
2786
|
+
await ensureDirExists(dirname3(absPath));
|
|
2787
|
+
writeFileSync8(absPath, doc.content, "utf8");
|
|
2788
|
+
downloaded += 1;
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
let deleted = 0;
|
|
2792
|
+
for (const path19 of deleteLocal) {
|
|
2793
|
+
const absPath = toFsPath(
|
|
2794
|
+
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
2917
2795
|
);
|
|
2918
|
-
if (
|
|
2919
|
-
|
|
2796
|
+
if (existsSync7(absPath)) {
|
|
2797
|
+
rmSync3(absPath, { force: true });
|
|
2798
|
+
deleted += 1;
|
|
2920
2799
|
}
|
|
2921
|
-
} catch {
|
|
2922
2800
|
}
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
writeFileSync9(
|
|
2927
|
-
toFsPath(join9(rulesDir, MANIFEST_FILE3)),
|
|
2928
|
-
`${JSON.stringify(manifest, null, 2)}
|
|
2801
|
+
writeFileSync8(
|
|
2802
|
+
toFsPath(join9(docsDir, MANIFEST_FILE3)),
|
|
2803
|
+
`${JSON.stringify(remoteManifest, null, 2)}
|
|
2929
2804
|
`,
|
|
2930
2805
|
"utf8"
|
|
2931
2806
|
);
|
|
2807
|
+
console.log(
|
|
2808
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
2809
|
+
);
|
|
2810
|
+
return {
|
|
2811
|
+
synced: true,
|
|
2812
|
+
projectId,
|
|
2813
|
+
downloaded,
|
|
2814
|
+
deleted
|
|
2815
|
+
};
|
|
2932
2816
|
}
|
|
2933
|
-
function
|
|
2934
|
-
return listBaseRuleFileNames().includes(basename3(fileName));
|
|
2935
|
-
}
|
|
2936
|
-
function isRuleUpToDate(entry, rule, dest) {
|
|
2937
|
-
if (!entry || !existsSync7(toFsPath(dest))) return false;
|
|
2938
|
-
if (entry.fileName !== ruleLocalFileName(rule.name)) return false;
|
|
2939
|
-
const updatedAt = rule.updatedAt ?? "";
|
|
2940
|
-
if (entry.updatedAt !== updatedAt) return false;
|
|
2941
|
-
const localContent = readFileSync7(toFsPath(dest), "utf8");
|
|
2942
|
-
return localContent === (rule.content ?? "");
|
|
2943
|
-
}
|
|
2944
|
-
async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
2817
|
+
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
2945
2818
|
const api = createApmApiClient(cfg);
|
|
2946
|
-
const
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
);
|
|
2954
|
-
return { written: [], skipped: [], removed: [], repositoryId: null };
|
|
2819
|
+
const { projectId } = await resolveProjectIdForSync(
|
|
2820
|
+
api,
|
|
2821
|
+
workdirPath,
|
|
2822
|
+
options
|
|
2823
|
+
);
|
|
2824
|
+
if (!projectId) {
|
|
2825
|
+
return 0;
|
|
2955
2826
|
}
|
|
2956
|
-
const
|
|
2957
|
-
const
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2827
|
+
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
2828
|
+
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
2829
|
+
if (localPaths.length === 0) {
|
|
2830
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2831
|
+
return 0;
|
|
2832
|
+
}
|
|
2833
|
+
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
2834
|
+
const remoteHashByPath = new Map(
|
|
2835
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2836
|
+
);
|
|
2837
|
+
const remoteDescriptionByPath = new Map(
|
|
2838
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
2839
|
+
);
|
|
2840
|
+
let synced = 0;
|
|
2841
|
+
for (const path19 of localPaths) {
|
|
2842
|
+
const absPath = toFsPath(
|
|
2843
|
+
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
2844
|
+
);
|
|
2845
|
+
const content = readFileSync6(absPath, "utf8");
|
|
2846
|
+
const contentHash = hashLocalFileContent(content);
|
|
2847
|
+
if (remoteHashByPath.get(path19) === contentHash) {
|
|
2972
2848
|
continue;
|
|
2973
2849
|
}
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2850
|
+
await api.cli.upsertProjectDocument({
|
|
2851
|
+
projectId,
|
|
2852
|
+
path: path19,
|
|
2853
|
+
content,
|
|
2854
|
+
description: remoteDescriptionByPath.get(path19) ?? void 0
|
|
2855
|
+
});
|
|
2856
|
+
synced += 1;
|
|
2857
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path19}`);
|
|
2978
2858
|
}
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
if (remoteIds.has(ruleId)) continue;
|
|
2982
|
-
if (isBaseRuleFileName(entry.fileName)) continue;
|
|
2983
|
-
const dest = join9(rulesDir, entry.fileName);
|
|
2984
|
-
if (existsSync7(toFsPath(dest))) {
|
|
2985
|
-
rmSync3(toFsPath(dest), { force: true });
|
|
2986
|
-
}
|
|
2987
|
-
removed.push(entry.fileName);
|
|
2988
|
-
console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u5E73\u53F0\u89C4\u5219: rules/${entry.fileName}`);
|
|
2859
|
+
if (synced === 0) {
|
|
2860
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2989
2861
|
}
|
|
2990
|
-
|
|
2991
|
-
return { written, skipped, removed, repositoryId };
|
|
2862
|
+
return synced;
|
|
2992
2863
|
}
|
|
2993
2864
|
|
|
2994
2865
|
// src/commands/pull.ts
|
|
@@ -3012,20 +2883,20 @@ async function runPull(sessionId, remoteWorkdir) {
|
|
|
3012
2883
|
const dir = sessionDir(trimmedId, apmRoot);
|
|
3013
2884
|
const docsDir = sessionDocsDir(trimmedId, apmRoot);
|
|
3014
2885
|
await ensureDirExists(docsDir);
|
|
3015
|
-
|
|
2886
|
+
writeFileSync9(
|
|
3016
2887
|
sessionRulePath(trimmedId, apmRoot),
|
|
3017
2888
|
detail.description ?? "",
|
|
3018
2889
|
"utf8"
|
|
3019
2890
|
);
|
|
3020
|
-
|
|
2891
|
+
writeFileSync9(
|
|
3021
2892
|
sessionTaskPath(trimmedId, apmRoot),
|
|
3022
2893
|
detail.task.description ?? "",
|
|
3023
2894
|
"utf8"
|
|
3024
2895
|
);
|
|
3025
|
-
|
|
2896
|
+
writeFileSync9(sessionTodoPath(trimmedId, apmRoot), detail.todo ?? "", "utf8");
|
|
3026
2897
|
for (const doc of documents) {
|
|
3027
2898
|
const fileName = documentLocalFileName(doc.name);
|
|
3028
|
-
|
|
2899
|
+
writeFileSync9(join10(docsDir, fileName), doc.content ?? "", "utf8");
|
|
3029
2900
|
}
|
|
3030
2901
|
const sessionYaml = yamlStringify(
|
|
3031
2902
|
{
|
|
@@ -3042,13 +2913,13 @@ async function runPull(sessionId, remoteWorkdir) {
|
|
|
3042
2913
|
},
|
|
3043
2914
|
{ lineWidth: 0 }
|
|
3044
2915
|
);
|
|
3045
|
-
|
|
2916
|
+
writeFileSync9(
|
|
3046
2917
|
sessionYamlPath(trimmedId, apmRoot),
|
|
3047
2918
|
sessionYaml.endsWith("\n") ? sessionYaml : `${sessionYaml}
|
|
3048
2919
|
`,
|
|
3049
2920
|
"utf8"
|
|
3050
2921
|
);
|
|
3051
|
-
|
|
2922
|
+
writeFileSync9(
|
|
3052
2923
|
sessionMessagesXmlPath(trimmedId, apmRoot),
|
|
3053
2924
|
formatSessionMessagesXml(trimmedId, messages),
|
|
3054
2925
|
"utf8"
|
|
@@ -3065,7 +2936,7 @@ async function runPull(sessionId, remoteWorkdir) {
|
|
|
3065
2936
|
import { spawnSync } from "child_process";
|
|
3066
2937
|
|
|
3067
2938
|
// src/version.ts
|
|
3068
|
-
import { readFileSync as
|
|
2939
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
3069
2940
|
import { dirname as dirname4, join as join11 } from "path";
|
|
3070
2941
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3071
2942
|
var CLI_PACKAGE_NAME = "ai-project-manage-cli";
|
|
@@ -3073,7 +2944,7 @@ function readCliVersion() {
|
|
|
3073
2944
|
try {
|
|
3074
2945
|
const dir = dirname4(fileURLToPath2(import.meta.url));
|
|
3075
2946
|
const pkgPath = join11(dir, "..", "package.json");
|
|
3076
|
-
const pkg = JSON.parse(
|
|
2947
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
3077
2948
|
return pkg.version ?? "0.0.0";
|
|
3078
2949
|
} catch {
|
|
3079
2950
|
return "0.0.0";
|
|
@@ -3328,7 +3199,7 @@ async function runSyncProjectDocuments(options) {
|
|
|
3328
3199
|
}
|
|
3329
3200
|
|
|
3330
3201
|
// src/commands/sync-document.ts
|
|
3331
|
-
import { existsSync as existsSync11, readFileSync as
|
|
3202
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
3332
3203
|
import { basename as basename4 } from "path";
|
|
3333
3204
|
|
|
3334
3205
|
// src/assumptions/local-validate.ts
|
|
@@ -3499,7 +3370,7 @@ init_client();
|
|
|
3499
3370
|
|
|
3500
3371
|
// src/commands/sync-session-documents.ts
|
|
3501
3372
|
init_client();
|
|
3502
|
-
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as
|
|
3373
|
+
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync8 } from "fs";
|
|
3503
3374
|
import { join as join13 } from "path";
|
|
3504
3375
|
function listLocalMarkdownFiles(docsDir) {
|
|
3505
3376
|
if (!existsSync10(docsDir)) {
|
|
@@ -3518,7 +3389,7 @@ function remoteDocumentByLocalName(remoteDocuments, localFileName) {
|
|
|
3518
3389
|
}
|
|
3519
3390
|
async function upsertLocalDocumentFile(api, sessionId, docsDir, fileName) {
|
|
3520
3391
|
const absPath = join13(docsDir, fileName);
|
|
3521
|
-
const content =
|
|
3392
|
+
const content = readFileSync8(absPath, "utf8");
|
|
3522
3393
|
const name = documentPlatformName(absPath);
|
|
3523
3394
|
return api.cli.upsertDocument({
|
|
3524
3395
|
sessionId,
|
|
@@ -3541,7 +3412,7 @@ async function syncSessionDocuments(cfg, sessionId, apmRoot, options) {
|
|
|
3541
3412
|
let synced = 0;
|
|
3542
3413
|
for (const fileName of localFiles) {
|
|
3543
3414
|
const absPath = join13(docsDir, fileName);
|
|
3544
|
-
const content =
|
|
3415
|
+
const content = readFileSync8(absPath, "utf8");
|
|
3545
3416
|
const remote = remoteDocumentByLocalName(remoteDocuments, fileName);
|
|
3546
3417
|
if (remote && remote.content === content) {
|
|
3547
3418
|
continue;
|
|
@@ -3585,7 +3456,7 @@ async function runSyncDocument(sessionId, options) {
|
|
|
3585
3456
|
const fileName = basename4(absPath);
|
|
3586
3457
|
const assumptionSource = resolveAssumptionSourceFromFileName(fileName);
|
|
3587
3458
|
if (assumptionSource) {
|
|
3588
|
-
const content =
|
|
3459
|
+
const content = readFileSync9(absPath, "utf8");
|
|
3589
3460
|
const validation = validateAssumptionsMarkdown(content, assumptionSource);
|
|
3590
3461
|
if (!validation.ok) {
|
|
3591
3462
|
printAssumptionValidationResult(validation);
|
|
@@ -3948,7 +3819,7 @@ init_client();
|
|
|
3948
3819
|
import path11 from "node:path";
|
|
3949
3820
|
|
|
3950
3821
|
// src/commands/deploy/internal/apm-config.ts
|
|
3951
|
-
import { existsSync as existsSync13, readFileSync as
|
|
3822
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
|
|
3952
3823
|
import { resolve as resolve5 } from "node:path";
|
|
3953
3824
|
|
|
3954
3825
|
// src/commands/deploy/internal/config/config-validation.ts
|
|
@@ -4041,7 +3912,7 @@ function resolveFrontendDeployFromApmConfig(cfg) {
|
|
|
4041
3912
|
}
|
|
4042
3913
|
|
|
4043
3914
|
// src/commands/deploy/internal/config/maven-repo.ts
|
|
4044
|
-
import { existsSync as existsSync12, readFileSync as
|
|
3915
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
|
|
4045
3916
|
import { homedir as homedir2 } from "node:os";
|
|
4046
3917
|
import { join as join14, resolve as resolve4 } from "node:path";
|
|
4047
3918
|
var MAVEN_REPO_ENV_KEYS = [
|
|
@@ -4105,7 +3976,7 @@ function readMavenLocalRepoFromSettings() {
|
|
|
4105
3976
|
return null;
|
|
4106
3977
|
}
|
|
4107
3978
|
try {
|
|
4108
|
-
const xml =
|
|
3979
|
+
const xml = readFileSync10(settingsPath, "utf8");
|
|
4109
3980
|
const match = xml.match(
|
|
4110
3981
|
/<localRepository>\s*([^<]+?)\s*<\/localRepository>/
|
|
4111
3982
|
);
|
|
@@ -4241,7 +4112,7 @@ function loadApmConfig(options) {
|
|
|
4241
4112
|
process.exit(1);
|
|
4242
4113
|
}
|
|
4243
4114
|
try {
|
|
4244
|
-
const raw =
|
|
4115
|
+
const raw = readFileSync11(p, "utf8");
|
|
4245
4116
|
return JSON.parse(raw);
|
|
4246
4117
|
} catch (e) {
|
|
4247
4118
|
console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
|
|
@@ -4412,7 +4283,7 @@ import { dirname as dirname5, join as join17 } from "node:path";
|
|
|
4412
4283
|
init_config();
|
|
4413
4284
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
4414
4285
|
import { setTimeout as delay } from "node:timers/promises";
|
|
4415
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as
|
|
4286
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
4416
4287
|
import { join as join16 } from "path";
|
|
4417
4288
|
|
|
4418
4289
|
// src/commands/connect-lock.ts
|
|
@@ -4420,9 +4291,9 @@ init_config();
|
|
|
4420
4291
|
import {
|
|
4421
4292
|
existsSync as existsSync14,
|
|
4422
4293
|
mkdirSync as mkdirSync6,
|
|
4423
|
-
readFileSync as
|
|
4294
|
+
readFileSync as readFileSync12,
|
|
4424
4295
|
unlinkSync,
|
|
4425
|
-
writeFileSync as
|
|
4296
|
+
writeFileSync as writeFileSync10
|
|
4426
4297
|
} from "fs";
|
|
4427
4298
|
import { join as join15 } from "path";
|
|
4428
4299
|
var CONNECT_LOCK_PATH = join15(APM_CONFIG_DIR, "connect.lock");
|
|
@@ -4454,7 +4325,7 @@ function waitForPm2LockHandoff(timeoutMs = 5e3) {
|
|
|
4454
4325
|
function readConnectLock() {
|
|
4455
4326
|
if (!existsSync14(CONNECT_LOCK_PATH)) return null;
|
|
4456
4327
|
try {
|
|
4457
|
-
const raw =
|
|
4328
|
+
const raw = readFileSync12(CONNECT_LOCK_PATH, "utf8");
|
|
4458
4329
|
const parsed = JSON.parse(raw);
|
|
4459
4330
|
if (typeof parsed.pid !== "number" || parsed.mode !== "foreground" && parsed.mode !== "pm2" || typeof parsed.startedAt !== "string") {
|
|
4460
4331
|
return null;
|
|
@@ -4495,7 +4366,7 @@ function acquireConnectLock(mode) {
|
|
|
4495
4366
|
mode,
|
|
4496
4367
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4497
4368
|
};
|
|
4498
|
-
|
|
4369
|
+
writeFileSync10(
|
|
4499
4370
|
CONNECT_LOCK_PATH,
|
|
4500
4371
|
JSON.stringify(lock, null, 2) + "\n",
|
|
4501
4372
|
"utf8"
|
|
@@ -5009,7 +4880,7 @@ async function resolveBaseUrl(server) {
|
|
|
5009
4880
|
}
|
|
5010
4881
|
function writeConnectLaunchFiles(options) {
|
|
5011
4882
|
mkdirSync7(APM_CONFIG_DIR, { recursive: true });
|
|
5012
|
-
|
|
4883
|
+
writeFileSync11(PM2_CONNECT_ENTRY_PATH, CONNECT_ENTRY_SCRIPT, "utf8");
|
|
5013
4884
|
const env = {
|
|
5014
4885
|
...collectPm2LaunchEnv()
|
|
5015
4886
|
};
|
|
@@ -5024,7 +4895,7 @@ function writeConnectLaunchFiles(options) {
|
|
|
5024
4895
|
cwd: options.cwd,
|
|
5025
4896
|
env
|
|
5026
4897
|
};
|
|
5027
|
-
|
|
4898
|
+
writeFileSync11(
|
|
5028
4899
|
PM2_CONNECT_LAUNCH_PATH,
|
|
5029
4900
|
JSON.stringify(launch, null, 2) + "\n",
|
|
5030
4901
|
"utf8"
|
|
@@ -5036,7 +4907,7 @@ function writeEcosystemFile(options) {
|
|
|
5036
4907
|
entryScript: PM2_CONNECT_ENTRY_PATH,
|
|
5037
4908
|
baseUrl: options.baseUrl
|
|
5038
4909
|
});
|
|
5039
|
-
|
|
4910
|
+
writeFileSync11(
|
|
5040
4911
|
PM2_ECOSYSTEM_PATH,
|
|
5041
4912
|
formatConnectPm2EcosystemFile(ecosystem),
|
|
5042
4913
|
"utf8"
|
|
@@ -5457,7 +5328,7 @@ function logWisdomFrontendDeployContext(input) {
|
|
|
5457
5328
|
}
|
|
5458
5329
|
|
|
5459
5330
|
// src/commands/deploy/internal/wisdom-deploy.ts
|
|
5460
|
-
import { existsSync as existsSync21, readFileSync as
|
|
5331
|
+
import { existsSync as existsSync21, readFileSync as readFileSync15, statSync as statSync10 } from "node:fs";
|
|
5461
5332
|
import path10 from "node:path";
|
|
5462
5333
|
|
|
5463
5334
|
// src/commands/deploy/deploy-shell-run.ts
|
|
@@ -5557,9 +5428,9 @@ import path9 from "node:path";
|
|
|
5557
5428
|
import {
|
|
5558
5429
|
mkdirSync as mkdirSync9,
|
|
5559
5430
|
readdirSync as readdirSync6,
|
|
5560
|
-
readFileSync as
|
|
5431
|
+
readFileSync as readFileSync14,
|
|
5561
5432
|
statSync as statSync8,
|
|
5562
|
-
writeFileSync as
|
|
5433
|
+
writeFileSync as writeFileSync13
|
|
5563
5434
|
} from "node:fs";
|
|
5564
5435
|
import path6 from "node:path";
|
|
5565
5436
|
import JSZip2 from "jszip";
|
|
@@ -5568,9 +5439,9 @@ import JSZip2 from "jszip";
|
|
|
5568
5439
|
import {
|
|
5569
5440
|
existsSync as existsSync18,
|
|
5570
5441
|
mkdirSync as mkdirSync8,
|
|
5571
|
-
readFileSync as
|
|
5442
|
+
readFileSync as readFileSync13,
|
|
5572
5443
|
statSync as statSync7,
|
|
5573
|
-
writeFileSync as
|
|
5444
|
+
writeFileSync as writeFileSync12
|
|
5574
5445
|
} from "node:fs";
|
|
5575
5446
|
import path2 from "node:path";
|
|
5576
5447
|
function deployCacheDir() {
|
|
@@ -5591,12 +5462,12 @@ function loadManifest3() {
|
|
|
5591
5462
|
if (!existsSync18(manifestPath3)) {
|
|
5592
5463
|
return {};
|
|
5593
5464
|
}
|
|
5594
|
-
return JSON.parse(
|
|
5465
|
+
return JSON.parse(readFileSync13(manifestPath3, "utf8"));
|
|
5595
5466
|
}
|
|
5596
5467
|
function saveManifest3(manifest) {
|
|
5597
5468
|
const dir = deployCacheDir();
|
|
5598
5469
|
mkdirSync8(dir, { recursive: true });
|
|
5599
|
-
|
|
5470
|
+
writeFileSync12(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
|
|
5600
5471
|
}
|
|
5601
5472
|
function updateManifestEntries(manifest, entries, projectRoot) {
|
|
5602
5473
|
for (const entry of entries) {
|
|
@@ -6010,7 +5881,7 @@ async function createUpdatePackage(entries, packageName) {
|
|
|
6010
5881
|
log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path6.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
|
|
6011
5882
|
const zip = new JSZip2();
|
|
6012
5883
|
for (const entry of entries) {
|
|
6013
|
-
const content =
|
|
5884
|
+
const content = readFileSync14(entry.path);
|
|
6014
5885
|
zip.file(entry.arcname, content);
|
|
6015
5886
|
log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
|
|
6016
5887
|
}
|
|
@@ -6019,7 +5890,7 @@ async function createUpdatePackage(entries, packageName) {
|
|
|
6019
5890
|
compression: "DEFLATE",
|
|
6020
5891
|
compressionOptions: { level: 6 }
|
|
6021
5892
|
});
|
|
6022
|
-
|
|
5893
|
+
writeFileSync13(zipPath, buffer);
|
|
6023
5894
|
return zipPath;
|
|
6024
5895
|
}
|
|
6025
5896
|
function listAllLibFilesForArchive(libDir) {
|
|
@@ -6593,7 +6464,7 @@ function readPackageScripts(cwd) {
|
|
|
6593
6464
|
return {};
|
|
6594
6465
|
}
|
|
6595
6466
|
try {
|
|
6596
|
-
const raw =
|
|
6467
|
+
const raw = readFileSync15(pkgPath, "utf8");
|
|
6597
6468
|
const parsed = JSON.parse(raw);
|
|
6598
6469
|
return parsed.scripts ?? {};
|
|
6599
6470
|
} catch {
|
|
@@ -7261,7 +7132,7 @@ import { resolve as resolve7 } from "node:path";
|
|
|
7261
7132
|
import { getDefaultSdkStateRoot } from "@cursor/sdk";
|
|
7262
7133
|
|
|
7263
7134
|
// src/commands/connect/webide-agent-registry.ts
|
|
7264
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as
|
|
7135
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "node:fs";
|
|
7265
7136
|
import { dirname as dirname7, resolve as resolve6 } from "node:path";
|
|
7266
7137
|
function registryPath(workdir, taskId) {
|
|
7267
7138
|
return resolve6(workdir, ".apm", "webide", taskId, "cursor-agent.json");
|
|
@@ -7271,7 +7142,7 @@ function readRegistry(path19) {
|
|
|
7271
7142
|
return {};
|
|
7272
7143
|
}
|
|
7273
7144
|
try {
|
|
7274
|
-
const parsed = JSON.parse(
|
|
7145
|
+
const parsed = JSON.parse(readFileSync16(path19, "utf8"));
|
|
7275
7146
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7276
7147
|
const raw = parsed;
|
|
7277
7148
|
const state = {};
|
|
@@ -7756,7 +7627,7 @@ ${JSON.stringify(event, null, 2)}
|
|
|
7756
7627
|
}
|
|
7757
7628
|
|
|
7758
7629
|
// src/commands/connect/agent-session-registry.ts
|
|
7759
|
-
import { existsSync as existsSync25, mkdirSync as mkdirSync11, readFileSync as
|
|
7630
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync11, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "node:fs";
|
|
7760
7631
|
import { dirname as dirname8, resolve as resolve8 } from "node:path";
|
|
7761
7632
|
function registryPath2(workdir, sessionId) {
|
|
7762
7633
|
return resolve8(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
|
|
@@ -7766,7 +7637,7 @@ function readRegistry2(path19) {
|
|
|
7766
7637
|
return {};
|
|
7767
7638
|
}
|
|
7768
7639
|
try {
|
|
7769
|
-
const parsed = JSON.parse(
|
|
7640
|
+
const parsed = JSON.parse(readFileSync17(path19, "utf8"));
|
|
7770
7641
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7771
7642
|
const result = {};
|
|
7772
7643
|
for (const [key, value] of Object.entries(
|
|
@@ -7784,7 +7655,7 @@ function readRegistry2(path19) {
|
|
|
7784
7655
|
}
|
|
7785
7656
|
function writeRegistry(path19, registry) {
|
|
7786
7657
|
mkdirSync11(dirname8(path19), { recursive: true });
|
|
7787
|
-
|
|
7658
|
+
writeFileSync15(path19, `${JSON.stringify(registry, null, 2)}
|
|
7788
7659
|
`, "utf8");
|
|
7789
7660
|
}
|
|
7790
7661
|
function loadSessionAgentId(workdir, sessionId, user) {
|
|
@@ -8682,7 +8553,7 @@ async function ensureMessageHasReply(cfg, sessionId, messageId, fallback) {
|
|
|
8682
8553
|
}
|
|
8683
8554
|
|
|
8684
8555
|
// src/commands/connect/cli-version-sync.ts
|
|
8685
|
-
import { existsSync as existsSync26, readFileSync as
|
|
8556
|
+
import { existsSync as existsSync26, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
|
|
8686
8557
|
import { join as join19 } from "path";
|
|
8687
8558
|
var CLI_VERSION_FILE = ".cli-version.json";
|
|
8688
8559
|
function manifestPath2(apmDir) {
|
|
@@ -8695,7 +8566,7 @@ function loadManifest4(apmDir) {
|
|
|
8695
8566
|
}
|
|
8696
8567
|
try {
|
|
8697
8568
|
const parsed = JSON.parse(
|
|
8698
|
-
|
|
8569
|
+
readFileSync18(path19, "utf8")
|
|
8699
8570
|
);
|
|
8700
8571
|
if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
|
|
8701
8572
|
return parsed;
|
|
@@ -8706,7 +8577,7 @@ function loadManifest4(apmDir) {
|
|
|
8706
8577
|
}
|
|
8707
8578
|
function saveManifest4(apmDir, cliVersion) {
|
|
8708
8579
|
const manifest = { version: 1, cliVersion };
|
|
8709
|
-
|
|
8580
|
+
writeFileSync16(
|
|
8710
8581
|
toFsPath(manifestPath2(apmDir)),
|
|
8711
8582
|
`${JSON.stringify(manifest, null, 2)}
|
|
8712
8583
|
`,
|
|
@@ -8983,13 +8854,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
8983
8854
|
};
|
|
8984
8855
|
try {
|
|
8985
8856
|
if (signal.aborted) return;
|
|
8986
|
-
|
|
8987
|
-
"workspace-init",
|
|
8988
|
-
() => ensureWorkspaceInitialized(workdir)
|
|
8989
|
-
);
|
|
8990
|
-
if (!didInit) {
|
|
8991
|
-
assertApmGitignoredInRepo(workdir);
|
|
8992
|
-
}
|
|
8857
|
+
await runStep("workspace-init", () => ensureWorkspaceInitialized(workdir));
|
|
8993
8858
|
if (shouldRunBranch(msg.sessionId, workdir)) {
|
|
8994
8859
|
if (signal.aborted) return;
|
|
8995
8860
|
await runStep("branch", () => runBranch(msg.sessionId, { cwd: workdir }));
|
|
@@ -9862,7 +9727,7 @@ import path15 from "node:path";
|
|
|
9862
9727
|
import Docker from "dockerode";
|
|
9863
9728
|
|
|
9864
9729
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
|
|
9865
|
-
import { existsSync as existsSync27, readFileSync as
|
|
9730
|
+
import { existsSync as existsSync27, readFileSync as readFileSync19 } from "node:fs";
|
|
9866
9731
|
import path12 from "node:path";
|
|
9867
9732
|
function asOptionalTlsBuffer(value) {
|
|
9868
9733
|
if (typeof value !== "string") {
|
|
@@ -9875,7 +9740,7 @@ function asOptionalTlsBuffer(value) {
|
|
|
9875
9740
|
return void 0;
|
|
9876
9741
|
}
|
|
9877
9742
|
if (existsSync27(normalized)) {
|
|
9878
|
-
return
|
|
9743
|
+
return readFileSync19(normalized);
|
|
9879
9744
|
}
|
|
9880
9745
|
const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
|
|
9881
9746
|
if (looksLikePath) {
|
|
@@ -10085,7 +9950,7 @@ var DockerodeClient = class {
|
|
|
10085
9950
|
var createDockerodeClient = (config) => new DockerodeClient(config);
|
|
10086
9951
|
|
|
10087
9952
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
|
|
10088
|
-
import { existsSync as existsSync28, readFileSync as
|
|
9953
|
+
import { existsSync as existsSync28, readFileSync as readFileSync20, statSync as statSync11 } from "node:fs";
|
|
10089
9954
|
import path13 from "node:path";
|
|
10090
9955
|
function stripSurroundingQuotes(value) {
|
|
10091
9956
|
const t = value.trim();
|
|
@@ -10105,7 +9970,7 @@ function loadEnvFromFile(envFilePath) {
|
|
|
10105
9970
|
if (!existsSync28(targetPath) || !statSync11(targetPath).isFile()) {
|
|
10106
9971
|
return {};
|
|
10107
9972
|
}
|
|
10108
|
-
const raw =
|
|
9973
|
+
const raw = readFileSync20(targetPath, "utf-8");
|
|
10109
9974
|
const result = {};
|
|
10110
9975
|
for (const line of raw.split(/\r?\n/)) {
|
|
10111
9976
|
const normalized = line.trim();
|
|
@@ -10686,9 +10551,7 @@ function buildProgram() {
|
|
|
10686
10551
|
).option("--api-key <key>", "\u5BA2\u6237\u673A API Key\uFF08cm_ \u5F00\u5934\uFF09").option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u4F8B\u5982 http://127.0.0.1:3000").action(async (opts) => {
|
|
10687
10552
|
await runLogin(opts);
|
|
10688
10553
|
});
|
|
10689
|
-
program.command("init").description(
|
|
10690
|
-
"\u5728\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u521D\u59CB\u5316 .apm \u6A21\u677F\uFF08\u82E5 .apm \u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\u5219\u62A5\u9519\uFF09\uFF1B\u5DF2\u767B\u5F55\u4E14\u5E73\u53F0\u6709\u5339\u914D\u90E8\u7F72\u914D\u7F6E\u65F6\u4F1A\u540C\u6B65 apm.config.json"
|
|
10691
|
-
).option("--name <name>", "\u5DE5\u4F5C\u76EE\u5F55\u540D\u79F0").action(async (opts) => {
|
|
10554
|
+
program.command("init").description("\u5728\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u521B\u5EFA .apm \u5E76\u521D\u59CB\u5316\u6A21\u677F").option("--name <name>", "\u5DE5\u4F5C\u76EE\u5F55\u540D\u79F0").action(async (opts) => {
|
|
10692
10555
|
await runInit(opts.name);
|
|
10693
10556
|
});
|
|
10694
10557
|
program.command("update").description(
|