ai-project-manage-cli 7.1.37 → 7.1.39
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 +461 -389
- package/dist/webide-message-worker.js +456 -656
- package/package.json +1 -1
- package/template/AGENTS.webide.md +27 -0
package/dist/index.js
CHANGED
|
@@ -1000,8 +1000,8 @@ 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();
|
|
@@ -1145,8 +1145,12 @@ var WORKSPACE_TEMPLATE_SUBDIRS = [
|
|
|
1145
1145
|
"rules",
|
|
1146
1146
|
"deploy"
|
|
1147
1147
|
];
|
|
1148
|
+
var WEBIDE_AGENTS_TEMPLATE_NAME = "AGENTS.webide.md";
|
|
1148
1149
|
function shouldSkipTemplateEntry(name) {
|
|
1149
|
-
return name === ".DS_Store" || name === "Thumbs.db";
|
|
1150
|
+
return name === ".DS_Store" || name === "Thumbs.db" || name === WEBIDE_AGENTS_TEMPLATE_NAME;
|
|
1151
|
+
}
|
|
1152
|
+
function isWebIdeRuleFileName(name) {
|
|
1153
|
+
return name.startsWith("webide");
|
|
1150
1154
|
}
|
|
1151
1155
|
function copyTemplateEntry(src, dest) {
|
|
1152
1156
|
const fsSrc = toFsPath(src);
|
|
@@ -1212,11 +1216,126 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
|
|
|
1212
1216
|
}
|
|
1213
1217
|
assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
|
|
1214
1218
|
}
|
|
1219
|
+
function listWebIdeRuleFileNames(rulesDir) {
|
|
1220
|
+
const fsRulesDir = toFsPath(rulesDir);
|
|
1221
|
+
if (!existsSync(fsRulesDir) || !statSync(fsRulesDir).isDirectory()) {
|
|
1222
|
+
return [];
|
|
1223
|
+
}
|
|
1224
|
+
return readdirSync(fsRulesDir).filter((name) => {
|
|
1225
|
+
if (shouldSkipTemplateEntry(name) || !isWebIdeRuleFileName(name)) {
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
return statSync(toFsPath(join2(rulesDir, name))).isFile();
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
function assertWebIdeTemplateCopiedToApm(apmDir, workdir) {
|
|
1232
|
+
const required = ["AGENTS.md", "apm.config.json", "rules"];
|
|
1233
|
+
for (const item of required) {
|
|
1234
|
+
const path19 = join2(apmDir, item);
|
|
1235
|
+
if (!existsSync(toFsPath(path19))) {
|
|
1236
|
+
throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path19}`);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
const rulesDir = join2(apmDir, "rules");
|
|
1240
|
+
const ruleNames = listWebIdeRuleFileNames(rulesDir);
|
|
1241
|
+
if (ruleNames.length === 0) {
|
|
1242
|
+
throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11 webide \u89C4\u5219: ${rulesDir}`);
|
|
1243
|
+
}
|
|
1244
|
+
const leakedRules = join2(workdir, "rules");
|
|
1245
|
+
const apmRules = join2(apmDir, "rules");
|
|
1246
|
+
if (existsSync(toFsPath(leakedRules)) && !existsSync(toFsPath(join2(apmRules, "webide_reply.md")))) {
|
|
1247
|
+
throw new Error(
|
|
1248
|
+
`[apm] \u6A21\u677F\u88AB\u590D\u5236\u5230\u9519\u8BEF\u4F4D\u7F6E: ${leakedRules}\uFF08\u5E94\u5728 ${apmRules}\uFF09`
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
async function copyWebIdeTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
|
|
1253
|
+
const resolvedTarget = resolve2(targetDir);
|
|
1254
|
+
const templateDir = resolve2(CLI_TEMPLATE_DIR);
|
|
1255
|
+
const fsTemplateDir = toFsPath(templateDir);
|
|
1256
|
+
if (!existsSync(fsTemplateDir) || !statSync(fsTemplateDir).isDirectory()) {
|
|
1257
|
+
throw new Error(`[apm] \u672A\u627E\u5230 CLI \u6A21\u677F\u76EE\u5F55: ${templateDir}`);
|
|
1258
|
+
}
|
|
1259
|
+
const agentsSrc = join2(templateDir, WEBIDE_AGENTS_TEMPLATE_NAME);
|
|
1260
|
+
if (!existsSync(toFsPath(agentsSrc))) {
|
|
1261
|
+
throw new Error(`[apm] \u672A\u627E\u5230 WebIDE \u6307\u5357: ${agentsSrc}`);
|
|
1262
|
+
}
|
|
1263
|
+
const rulesSrc = join2(templateDir, "rules");
|
|
1264
|
+
const ruleNames = listWebIdeRuleFileNames(rulesSrc);
|
|
1265
|
+
if (ruleNames.length === 0) {
|
|
1266
|
+
throw new Error(`[apm] CLI \u6A21\u677F\u4E2D\u6CA1\u6709 webide \u89C4\u5219: ${rulesSrc}`);
|
|
1267
|
+
}
|
|
1268
|
+
mkdirSync2(toFsPath(resolvedTarget), { recursive: true });
|
|
1269
|
+
copyFileSync(toFsPath(agentsSrc), toFsPath(join2(resolvedTarget, "AGENTS.md")));
|
|
1270
|
+
copyTemplateEntry(
|
|
1271
|
+
join2(templateDir, "apm.config.json"),
|
|
1272
|
+
join2(resolvedTarget, "apm.config.json")
|
|
1273
|
+
);
|
|
1274
|
+
const rulesDest = join2(resolvedTarget, "rules");
|
|
1275
|
+
mkdirSync2(toFsPath(rulesDest), { recursive: true });
|
|
1276
|
+
for (const name of ruleNames) {
|
|
1277
|
+
copyTemplateEntry(join2(rulesSrc, name), join2(rulesDest, name));
|
|
1278
|
+
}
|
|
1279
|
+
assertWebIdeTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// src/commands/init.ts
|
|
1283
|
+
function applyWorkspaceName(apmDir, name) {
|
|
1284
|
+
const trimmedName = name?.trim();
|
|
1285
|
+
if (!trimmedName) return;
|
|
1286
|
+
const apmConfigPath = toFsPath(join3(apmDir, "apm.config.json"));
|
|
1287
|
+
const config = readFileSync2(apmConfigPath, "utf8");
|
|
1288
|
+
const configJson = JSON.parse(config);
|
|
1289
|
+
configJson.name = trimmedName;
|
|
1290
|
+
writeFileSync2(
|
|
1291
|
+
apmConfigPath,
|
|
1292
|
+
`${JSON.stringify(configJson, null, 2)}
|
|
1293
|
+
`,
|
|
1294
|
+
"utf8"
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
async function initializeWorkspace(workdir, options) {
|
|
1298
|
+
await ensureWorkspaceApmDirForInit(workdir);
|
|
1299
|
+
const apmDir = workspaceApmDir(workdir);
|
|
1300
|
+
if (options?.webide) {
|
|
1301
|
+
await copyWebIdeTemplateFiles(apmDir, workdir);
|
|
1302
|
+
} else {
|
|
1303
|
+
await copyTemplateFiles(apmDir, workdir);
|
|
1304
|
+
}
|
|
1305
|
+
applyWorkspaceName(apmDir, options?.name);
|
|
1306
|
+
console.log(
|
|
1307
|
+
options?.webide ? `[apm] \u5DF2\u6309 WebIDE \u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}` : `[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`
|
|
1308
|
+
);
|
|
1309
|
+
return { didInit: true };
|
|
1310
|
+
}
|
|
1311
|
+
async function ensureWorkspaceInitialized(workdir, options) {
|
|
1312
|
+
if (isWorkspaceApmInitialized(workdir)) {
|
|
1313
|
+
return { didInit: false };
|
|
1314
|
+
}
|
|
1315
|
+
console.log(
|
|
1316
|
+
options?.webide ? `[apm] \u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u672A\u68C0\u6D4B\u5230\u5DF2\u521D\u59CB\u5316\u7684 .apm\uFF0C\u6B63\u5728\u6309 WebIDE \u81EA\u52A8\u521D\u59CB\u5316\u2026` : `[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`
|
|
1317
|
+
);
|
|
1318
|
+
return initializeWorkspace(workdir, options);
|
|
1319
|
+
}
|
|
1320
|
+
async function runInit(name, options) {
|
|
1321
|
+
const workdir = resolveWorkdirPath();
|
|
1322
|
+
await initializeWorkspace(workdir, {
|
|
1323
|
+
name,
|
|
1324
|
+
webide: options?.webide === true
|
|
1325
|
+
});
|
|
1326
|
+
console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// src/commands/login.ts
|
|
1330
|
+
init_config();
|
|
1331
|
+
init_client();
|
|
1332
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1333
|
+
import { ApiError } from "listpage-http";
|
|
1215
1334
|
|
|
1216
1335
|
// src/deployment-config-sync.ts
|
|
1217
1336
|
init_client();
|
|
1218
|
-
import { join as
|
|
1219
|
-
import { writeFileSync as
|
|
1337
|
+
import { join as join5 } from "path";
|
|
1338
|
+
import { writeFileSync as writeFileSync4 } from "fs";
|
|
1220
1339
|
|
|
1221
1340
|
// src/git-remote.ts
|
|
1222
1341
|
import { execFile } from "child_process";
|
|
@@ -1455,16 +1574,16 @@ init_config();
|
|
|
1455
1574
|
import {
|
|
1456
1575
|
existsSync as existsSync2,
|
|
1457
1576
|
mkdirSync as mkdirSync3,
|
|
1458
|
-
readFileSync as
|
|
1577
|
+
readFileSync as readFileSync3,
|
|
1459
1578
|
readdirSync as readdirSync2,
|
|
1460
1579
|
statSync as statSync2,
|
|
1461
|
-
writeFileSync as
|
|
1580
|
+
writeFileSync as writeFileSync3
|
|
1462
1581
|
} from "fs";
|
|
1463
|
-
import { basename as basename2, dirname as dirname2, join as
|
|
1582
|
+
import { basename as basename2, dirname as dirname2, join as join4, relative, resolve as resolve3 } from "path";
|
|
1464
1583
|
var WORKSPACE_REPOS_MANIFEST = "workspace-repos.json";
|
|
1465
1584
|
var WORKSPACE_REPOS_VERSION = 1;
|
|
1466
1585
|
function manifestPath(workdir) {
|
|
1467
|
-
return
|
|
1586
|
+
return join4(workspaceApmDir(workdir), WORKSPACE_REPOS_MANIFEST);
|
|
1468
1587
|
}
|
|
1469
1588
|
function absoluteRepoPath(workdir, entry) {
|
|
1470
1589
|
if (entry.path === "." || entry.path === "") {
|
|
@@ -1493,7 +1612,7 @@ function readManifest(workdir) {
|
|
|
1493
1612
|
}
|
|
1494
1613
|
try {
|
|
1495
1614
|
const raw = JSON.parse(
|
|
1496
|
-
|
|
1615
|
+
readFileSync3(path19, "utf8")
|
|
1497
1616
|
);
|
|
1498
1617
|
if (raw?.version !== WORKSPACE_REPOS_VERSION) {
|
|
1499
1618
|
return null;
|
|
@@ -1526,7 +1645,7 @@ function writeManifest(workdir, manifest) {
|
|
|
1526
1645
|
const apmDir = toFsPath(workspaceApmDir(workdir));
|
|
1527
1646
|
mkdirSync3(apmDir, { recursive: true });
|
|
1528
1647
|
const path19 = toFsPath(manifestPath(workdir));
|
|
1529
|
-
|
|
1648
|
+
writeFileSync3(path19, `${JSON.stringify(manifest, null, 2)}
|
|
1530
1649
|
`, "utf8");
|
|
1531
1650
|
}
|
|
1532
1651
|
function isPathInsideOrEqual(parentAbs, childAbs) {
|
|
@@ -1679,8 +1798,8 @@ async function writeDeploymentConfigContent(apmDir, content, configName) {
|
|
|
1679
1798
|
);
|
|
1680
1799
|
return false;
|
|
1681
1800
|
}
|
|
1682
|
-
const apmConfigPath = toFsPath(
|
|
1683
|
-
|
|
1801
|
+
const apmConfigPath = toFsPath(join5(apmDir, "apm.config.json"));
|
|
1802
|
+
writeFileSync4(apmConfigPath, `${JSON.stringify(parsed, null, 2)}
|
|
1684
1803
|
`, "utf8");
|
|
1685
1804
|
console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E: ${configName}`);
|
|
1686
1805
|
console.log("[apm] \u5DF2\u5199\u5165 .apm/apm.config.json");
|
|
@@ -1732,324 +1851,7 @@ ${diagnostic ?? ""}
|
|
|
1732
1851
|
return { synced: true, repositoryId, configName: config.name };
|
|
1733
1852
|
}
|
|
1734
1853
|
|
|
1735
|
-
// src/project-documents-sync.ts
|
|
1736
|
-
init_client();
|
|
1737
|
-
init_config();
|
|
1738
|
-
import {
|
|
1739
|
-
existsSync as existsSync3,
|
|
1740
|
-
readdirSync as readdirSync3,
|
|
1741
|
-
readFileSync as readFileSync3,
|
|
1742
|
-
rmSync,
|
|
1743
|
-
writeFileSync as writeFileSync4
|
|
1744
|
-
} from "fs";
|
|
1745
|
-
import { createHash } from "crypto";
|
|
1746
|
-
import { dirname as dirname3, join as join5, relative as relative2, sep } from "path";
|
|
1747
|
-
var MANIFEST_FILE = "manifest.json";
|
|
1748
|
-
function normalizeProjectIdForPath(projectId) {
|
|
1749
|
-
const id = projectId.trim();
|
|
1750
|
-
if (!id) {
|
|
1751
|
-
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1752
|
-
}
|
|
1753
|
-
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
1754
|
-
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
1755
|
-
}
|
|
1756
|
-
return id;
|
|
1757
|
-
}
|
|
1758
|
-
function projectDocumentsDir(apmRoot, projectId) {
|
|
1759
|
-
const id = normalizeProjectIdForPath(projectId);
|
|
1760
|
-
return join5(apmRoot ?? workspaceApmDir(), "project", id);
|
|
1761
|
-
}
|
|
1762
|
-
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
1763
|
-
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
1764
|
-
return join5(
|
|
1765
|
-
projectDocumentsDir(apmRoot, projectId),
|
|
1766
|
-
...normalized.split("/")
|
|
1767
|
-
);
|
|
1768
|
-
}
|
|
1769
|
-
function normalizeLocalDocumentPath(path19) {
|
|
1770
|
-
const trimmed = path19.trim().replace(/\\/g, "/");
|
|
1771
|
-
if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
|
|
1772
|
-
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path19}`);
|
|
1773
|
-
}
|
|
1774
|
-
const segments = trimmed.split("/").filter(Boolean);
|
|
1775
|
-
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
1776
|
-
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path19}`);
|
|
1777
|
-
}
|
|
1778
|
-
return segments.join("/");
|
|
1779
|
-
}
|
|
1780
|
-
function hashLocalFileContent(content) {
|
|
1781
|
-
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
1782
|
-
}
|
|
1783
|
-
function readLocalManifest(apmRoot, projectId) {
|
|
1784
|
-
const manifestPath3 = join5(
|
|
1785
|
-
projectDocumentsDir(apmRoot, projectId),
|
|
1786
|
-
MANIFEST_FILE
|
|
1787
|
-
);
|
|
1788
|
-
if (!existsSync3(manifestPath3)) {
|
|
1789
|
-
return null;
|
|
1790
|
-
}
|
|
1791
|
-
try {
|
|
1792
|
-
return JSON.parse(
|
|
1793
|
-
readFileSync3(manifestPath3, "utf8")
|
|
1794
|
-
);
|
|
1795
|
-
} catch {
|
|
1796
|
-
return null;
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
1800
|
-
const root = projectDocumentsDir(apmRoot, projectId);
|
|
1801
|
-
if (!existsSync3(root)) {
|
|
1802
|
-
return [];
|
|
1803
|
-
}
|
|
1804
|
-
const paths = [];
|
|
1805
|
-
const walk = (dir) => {
|
|
1806
|
-
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
1807
|
-
const abs = join5(dir, entry.name);
|
|
1808
|
-
if (entry.isDirectory()) {
|
|
1809
|
-
walk(abs);
|
|
1810
|
-
continue;
|
|
1811
|
-
}
|
|
1812
|
-
if (entry.isFile() && entry.name === MANIFEST_FILE) {
|
|
1813
|
-
continue;
|
|
1814
|
-
}
|
|
1815
|
-
const rel = relative2(root, abs).split(sep).join("/");
|
|
1816
|
-
paths.push(rel);
|
|
1817
|
-
}
|
|
1818
|
-
};
|
|
1819
|
-
walk(root);
|
|
1820
|
-
return paths.sort();
|
|
1821
|
-
}
|
|
1822
|
-
function diffManifestPaths(remote, local) {
|
|
1823
|
-
const remoteMap = new Map(
|
|
1824
|
-
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
1825
|
-
);
|
|
1826
|
-
const localMap = new Map(
|
|
1827
|
-
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
1828
|
-
);
|
|
1829
|
-
const download = [];
|
|
1830
|
-
for (const [path19, hash] of remoteMap) {
|
|
1831
|
-
if (localMap.get(path19) !== hash) {
|
|
1832
|
-
download.push(path19);
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
const deleteLocal = [];
|
|
1836
|
-
for (const path19 of localMap.keys()) {
|
|
1837
|
-
if (!remoteMap.has(path19)) {
|
|
1838
|
-
deleteLocal.push(path19);
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1841
|
-
return { download, deleteLocal };
|
|
1842
|
-
}
|
|
1843
|
-
async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
1844
|
-
const explicit = options?.projectId?.trim();
|
|
1845
|
-
if (explicit) {
|
|
1846
|
-
return { projectId: explicit, diagnostic: null };
|
|
1847
|
-
}
|
|
1848
|
-
try {
|
|
1849
|
-
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
1850
|
-
const projectId = baseline.projectId?.trim() || null;
|
|
1851
|
-
if (projectId) {
|
|
1852
|
-
return { projectId, diagnostic: null };
|
|
1853
|
-
}
|
|
1854
|
-
const projectIds = baseline.projectIds ?? [];
|
|
1855
|
-
if (projectIds.length === 0) {
|
|
1856
|
-
return {
|
|
1857
|
-
projectId: null,
|
|
1858
|
-
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
|
|
1859
|
-
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
1860
|
-
};
|
|
1861
|
-
}
|
|
1862
|
-
return {
|
|
1863
|
-
projectId: null,
|
|
1864
|
-
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
1865
|
-
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
1866
|
-
};
|
|
1867
|
-
} catch (err) {
|
|
1868
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1869
|
-
return {
|
|
1870
|
-
projectId: null,
|
|
1871
|
-
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
1872
|
-
};
|
|
1873
|
-
}
|
|
1874
|
-
}
|
|
1875
|
-
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
1876
|
-
const empty = {
|
|
1877
|
-
synced: false,
|
|
1878
|
-
projectId: null,
|
|
1879
|
-
downloaded: 0,
|
|
1880
|
-
deleted: 0
|
|
1881
|
-
};
|
|
1882
|
-
const cfg = await tryReadApmConfig();
|
|
1883
|
-
if (!cfg || !resolveApiKey(cfg)) {
|
|
1884
|
-
console.log(
|
|
1885
|
-
"[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"
|
|
1886
|
-
);
|
|
1887
|
-
return empty;
|
|
1888
|
-
}
|
|
1889
|
-
const api = createApmApiClient(cfg);
|
|
1890
|
-
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
1891
|
-
api,
|
|
1892
|
-
workdirPath,
|
|
1893
|
-
options
|
|
1894
|
-
);
|
|
1895
|
-
if (!projectId) {
|
|
1896
|
-
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
1897
|
-
${diagnostic ?? ""}`);
|
|
1898
|
-
return empty;
|
|
1899
|
-
}
|
|
1900
|
-
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
1901
|
-
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
1902
|
-
await ensureDirExists(docsDir);
|
|
1903
|
-
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
1904
|
-
{ projectId }
|
|
1905
|
-
);
|
|
1906
|
-
if (!remoteManifest) {
|
|
1907
|
-
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
1908
|
-
return { ...empty, projectId };
|
|
1909
|
-
}
|
|
1910
|
-
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
1911
|
-
const { download, deleteLocal } = diffManifestPaths(
|
|
1912
|
-
remoteManifest,
|
|
1913
|
-
localManifest
|
|
1914
|
-
);
|
|
1915
|
-
let downloaded = 0;
|
|
1916
|
-
if (download.length > 0) {
|
|
1917
|
-
const { list } = await api.cli.listProjectDocuments({
|
|
1918
|
-
projectId,
|
|
1919
|
-
paths: download.join(",")
|
|
1920
|
-
});
|
|
1921
|
-
for (const doc of list) {
|
|
1922
|
-
const absPath = toFsPath(
|
|
1923
|
-
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
1924
|
-
);
|
|
1925
|
-
await ensureDirExists(dirname3(absPath));
|
|
1926
|
-
writeFileSync4(absPath, doc.content, "utf8");
|
|
1927
|
-
downloaded += 1;
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
let deleted = 0;
|
|
1931
|
-
for (const path19 of deleteLocal) {
|
|
1932
|
-
const absPath = toFsPath(
|
|
1933
|
-
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
1934
|
-
);
|
|
1935
|
-
if (existsSync3(absPath)) {
|
|
1936
|
-
rmSync(absPath, { force: true });
|
|
1937
|
-
deleted += 1;
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
writeFileSync4(
|
|
1941
|
-
toFsPath(join5(docsDir, MANIFEST_FILE)),
|
|
1942
|
-
`${JSON.stringify(remoteManifest, null, 2)}
|
|
1943
|
-
`,
|
|
1944
|
-
"utf8"
|
|
1945
|
-
);
|
|
1946
|
-
console.log(
|
|
1947
|
-
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
1948
|
-
);
|
|
1949
|
-
return {
|
|
1950
|
-
synced: true,
|
|
1951
|
-
projectId,
|
|
1952
|
-
downloaded,
|
|
1953
|
-
deleted
|
|
1954
|
-
};
|
|
1955
|
-
}
|
|
1956
|
-
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
1957
|
-
const api = createApmApiClient(cfg);
|
|
1958
|
-
const { projectId } = await resolveProjectIdForSync(
|
|
1959
|
-
api,
|
|
1960
|
-
workdirPath,
|
|
1961
|
-
options
|
|
1962
|
-
);
|
|
1963
|
-
if (!projectId) {
|
|
1964
|
-
return 0;
|
|
1965
|
-
}
|
|
1966
|
-
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
1967
|
-
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
1968
|
-
if (localPaths.length === 0) {
|
|
1969
|
-
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
1970
|
-
return 0;
|
|
1971
|
-
}
|
|
1972
|
-
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
1973
|
-
const remoteHashByPath = new Map(
|
|
1974
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
1975
|
-
);
|
|
1976
|
-
const remoteDescriptionByPath = new Map(
|
|
1977
|
-
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
1978
|
-
);
|
|
1979
|
-
let synced = 0;
|
|
1980
|
-
for (const path19 of localPaths) {
|
|
1981
|
-
const absPath = toFsPath(
|
|
1982
|
-
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
1983
|
-
);
|
|
1984
|
-
const content = readFileSync3(absPath, "utf8");
|
|
1985
|
-
const contentHash = hashLocalFileContent(content);
|
|
1986
|
-
if (remoteHashByPath.get(path19) === contentHash) {
|
|
1987
|
-
continue;
|
|
1988
|
-
}
|
|
1989
|
-
await api.cli.upsertProjectDocument({
|
|
1990
|
-
projectId,
|
|
1991
|
-
path: path19,
|
|
1992
|
-
content,
|
|
1993
|
-
description: remoteDescriptionByPath.get(path19) ?? void 0
|
|
1994
|
-
});
|
|
1995
|
-
synced += 1;
|
|
1996
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path19}`);
|
|
1997
|
-
}
|
|
1998
|
-
if (synced === 0) {
|
|
1999
|
-
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2000
|
-
}
|
|
2001
|
-
return synced;
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
// src/commands/init.ts
|
|
2005
|
-
async function initializeWorkspace(workdir, options) {
|
|
2006
|
-
await ensureWorkspaceApmDirForInit(workdir);
|
|
2007
|
-
const apmDir = workspaceApmDir(workdir);
|
|
2008
|
-
await copyTemplateFiles(apmDir, workdir);
|
|
2009
|
-
const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
2010
|
-
await syncProjectDocumentsPull(workdir, apmDir);
|
|
2011
|
-
const trimmedName = options?.name?.trim();
|
|
2012
|
-
if (trimmedName) {
|
|
2013
|
-
const apmConfigPath = toFsPath(join6(apmDir, "apm.config.json"));
|
|
2014
|
-
const config = readFileSync4(apmConfigPath, "utf8");
|
|
2015
|
-
const configJson = JSON.parse(config);
|
|
2016
|
-
configJson.name = trimmedName;
|
|
2017
|
-
writeFileSync5(
|
|
2018
|
-
apmConfigPath,
|
|
2019
|
-
`${JSON.stringify(configJson, null, 2)}
|
|
2020
|
-
`,
|
|
2021
|
-
"utf8"
|
|
2022
|
-
);
|
|
2023
|
-
}
|
|
2024
|
-
console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
|
|
2025
|
-
return { didInit: true, syncResult };
|
|
2026
|
-
}
|
|
2027
|
-
async function ensureWorkspaceInitialized(workdir, options) {
|
|
2028
|
-
if (isWorkspaceApmInitialized(workdir)) {
|
|
2029
|
-
return { didInit: false };
|
|
2030
|
-
}
|
|
2031
|
-
console.log(
|
|
2032
|
-
`[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`
|
|
2033
|
-
);
|
|
2034
|
-
return initializeWorkspace(workdir, options);
|
|
2035
|
-
}
|
|
2036
|
-
async function runInit(name) {
|
|
2037
|
-
const workdir = resolveWorkdirPath();
|
|
2038
|
-
const { syncResult } = await initializeWorkspace(workdir, { name });
|
|
2039
|
-
console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
|
|
2040
|
-
if (syncResult && !syncResult.synced) {
|
|
2041
|
-
console.log(
|
|
2042
|
-
"[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"
|
|
2043
|
-
);
|
|
2044
|
-
}
|
|
2045
|
-
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");
|
|
2046
|
-
}
|
|
2047
|
-
|
|
2048
1854
|
// src/commands/login.ts
|
|
2049
|
-
init_config();
|
|
2050
|
-
init_client();
|
|
2051
|
-
import { existsSync as existsSync4 } from "fs";
|
|
2052
|
-
import { ApiError } from "listpage-http";
|
|
2053
1855
|
async function runLogin(opts) {
|
|
2054
1856
|
const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
2055
1857
|
const apiKey = (opts.apiKey?.trim() || process.env.APM_API_KEY?.trim() || "").replace(/^Bearer\s+/i, "");
|
|
@@ -2103,7 +1905,7 @@ async function runLogin(opts) {
|
|
|
2103
1905
|
);
|
|
2104
1906
|
const workdir = resolveWorkdirPath();
|
|
2105
1907
|
const apmDir = workspaceApmDir(workdir);
|
|
2106
|
-
if (
|
|
1908
|
+
if (existsSync3(apmDir)) {
|
|
2107
1909
|
await syncRemoteDeploymentConfig(workdir, apmDir);
|
|
2108
1910
|
}
|
|
2109
1911
|
}
|
|
@@ -2593,9 +2395,9 @@ function formatSessionMessagesXml(sessionId, messages) {
|
|
|
2593
2395
|
}
|
|
2594
2396
|
|
|
2595
2397
|
// src/commands/sync-session-attachments.ts
|
|
2596
|
-
import { existsSync as
|
|
2597
|
-
import { join as
|
|
2598
|
-
var
|
|
2398
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
2399
|
+
import { join as join6 } from "path";
|
|
2400
|
+
var MANIFEST_FILE = ".sync-manifest.json";
|
|
2599
2401
|
async function downloadAttachment(cfg, attachmentId) {
|
|
2600
2402
|
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
2601
2403
|
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({
|
|
@@ -2610,13 +2412,13 @@ async function downloadAttachment(cfg, attachmentId) {
|
|
|
2610
2412
|
return Buffer.from(await res.arrayBuffer());
|
|
2611
2413
|
}
|
|
2612
2414
|
function loadManifest(dir) {
|
|
2613
|
-
const path19 =
|
|
2614
|
-
if (!
|
|
2415
|
+
const path19 = join6(dir, MANIFEST_FILE);
|
|
2416
|
+
if (!existsSync4(path19)) {
|
|
2615
2417
|
return { version: 1, attachments: {} };
|
|
2616
2418
|
}
|
|
2617
2419
|
try {
|
|
2618
2420
|
const parsed = JSON.parse(
|
|
2619
|
-
|
|
2421
|
+
readFileSync4(path19, "utf8")
|
|
2620
2422
|
);
|
|
2621
2423
|
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
2622
2424
|
return parsed;
|
|
@@ -2626,15 +2428,15 @@ function loadManifest(dir) {
|
|
|
2626
2428
|
return { version: 1, attachments: {} };
|
|
2627
2429
|
}
|
|
2628
2430
|
function saveManifest(dir, manifest) {
|
|
2629
|
-
|
|
2630
|
-
|
|
2431
|
+
writeFileSync5(
|
|
2432
|
+
join6(dir, MANIFEST_FILE),
|
|
2631
2433
|
`${JSON.stringify(manifest, null, 2)}
|
|
2632
2434
|
`,
|
|
2633
2435
|
"utf8"
|
|
2634
2436
|
);
|
|
2635
2437
|
}
|
|
2636
2438
|
function isAttachmentUpToDate(entry, item, dest) {
|
|
2637
|
-
if (!entry || !
|
|
2439
|
+
if (!entry || !existsSync4(dest)) return false;
|
|
2638
2440
|
if (entry.name !== item.name) return false;
|
|
2639
2441
|
const createdAt = item.createdAt ?? "";
|
|
2640
2442
|
return entry.createdAt === createdAt;
|
|
@@ -2649,7 +2451,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
2649
2451
|
const nextManifest = { version: 1, attachments: {} };
|
|
2650
2452
|
const names = [];
|
|
2651
2453
|
for (const item of attachments) {
|
|
2652
|
-
const dest =
|
|
2454
|
+
const dest = join6(dir, item.name);
|
|
2653
2455
|
const entry = manifest.attachments[item.id];
|
|
2654
2456
|
const createdAt = item.createdAt ?? "";
|
|
2655
2457
|
names.push(item.name);
|
|
@@ -2659,7 +2461,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
2659
2461
|
continue;
|
|
2660
2462
|
}
|
|
2661
2463
|
const buffer = await downloadAttachment(cfg, item.id);
|
|
2662
|
-
|
|
2464
|
+
writeFileSync5(dest, buffer);
|
|
2663
2465
|
nextManifest.attachments[item.id] = {
|
|
2664
2466
|
name: item.name,
|
|
2665
2467
|
createdAt
|
|
@@ -2670,7 +2472,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
2670
2472
|
return names;
|
|
2671
2473
|
}
|
|
2672
2474
|
async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
2673
|
-
const dir =
|
|
2475
|
+
const dir = join6(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
|
|
2674
2476
|
return syncAttachmentsToDirectory(
|
|
2675
2477
|
cfg,
|
|
2676
2478
|
attachments,
|
|
@@ -2681,46 +2483,46 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
|
2681
2483
|
|
|
2682
2484
|
// src/rules-sync.ts
|
|
2683
2485
|
init_client();
|
|
2684
|
-
import { basename as basename3, extname as extname2, join as
|
|
2685
|
-
import { existsSync as
|
|
2486
|
+
import { basename as basename3, extname as extname2, join as join8 } from "path";
|
|
2487
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
2686
2488
|
|
|
2687
2489
|
// src/skills-sync.ts
|
|
2688
2490
|
import {
|
|
2689
2491
|
copyFileSync as copyFileSync2,
|
|
2690
2492
|
cpSync,
|
|
2691
|
-
existsSync as
|
|
2493
|
+
existsSync as existsSync5,
|
|
2692
2494
|
mkdirSync as mkdirSync4,
|
|
2693
|
-
readdirSync as
|
|
2694
|
-
rmSync
|
|
2495
|
+
readdirSync as readdirSync3,
|
|
2496
|
+
rmSync,
|
|
2695
2497
|
statSync as statSync3,
|
|
2696
|
-
writeFileSync as
|
|
2498
|
+
writeFileSync as writeFileSync6
|
|
2697
2499
|
} from "fs";
|
|
2698
|
-
import { join as
|
|
2699
|
-
var AGENTS_TEMPLATE_PATH =
|
|
2700
|
-
var BASE_SKILLS_TEMPLATE_DIR =
|
|
2701
|
-
var BASE_RULES_TEMPLATE_DIR =
|
|
2500
|
+
import { join as join7 } from "path";
|
|
2501
|
+
var AGENTS_TEMPLATE_PATH = join7(CLI_TEMPLATE_DIR, "AGENTS.md");
|
|
2502
|
+
var BASE_SKILLS_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "skills");
|
|
2503
|
+
var BASE_RULES_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "rules");
|
|
2702
2504
|
function sanitizeSkillDirName(name) {
|
|
2703
2505
|
const trimmed = name.trim();
|
|
2704
2506
|
if (!trimmed) return "skill";
|
|
2705
2507
|
return trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
2706
2508
|
}
|
|
2707
2509
|
function listBaseSkillDirNames() {
|
|
2708
|
-
if (!
|
|
2709
|
-
return
|
|
2710
|
-
const path19 =
|
|
2510
|
+
if (!existsSync5(BASE_SKILLS_TEMPLATE_DIR)) return [];
|
|
2511
|
+
return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
|
|
2512
|
+
const path19 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
2711
2513
|
return statSync3(path19).isDirectory();
|
|
2712
2514
|
});
|
|
2713
2515
|
}
|
|
2714
2516
|
function syncAgentsGuide(apmDir) {
|
|
2715
|
-
if (!
|
|
2517
|
+
if (!existsSync5(AGENTS_TEMPLATE_PATH)) return false;
|
|
2716
2518
|
mkdirSync4(apmDir, { recursive: true });
|
|
2717
|
-
copyFileSync2(AGENTS_TEMPLATE_PATH,
|
|
2519
|
+
copyFileSync2(AGENTS_TEMPLATE_PATH, join7(apmDir, "AGENTS.md"));
|
|
2718
2520
|
return true;
|
|
2719
2521
|
}
|
|
2720
2522
|
function listBaseRuleFileNames() {
|
|
2721
|
-
if (!
|
|
2722
|
-
return
|
|
2723
|
-
const path19 =
|
|
2523
|
+
if (!existsSync5(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
2524
|
+
return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
2525
|
+
const path19 = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
2724
2526
|
return statSync3(path19).isFile();
|
|
2725
2527
|
});
|
|
2726
2528
|
}
|
|
@@ -2728,8 +2530,8 @@ function syncBaseRules(rulesDir) {
|
|
|
2728
2530
|
mkdirSync4(rulesDir, { recursive: true });
|
|
2729
2531
|
const names = listBaseRuleFileNames();
|
|
2730
2532
|
for (const name of names) {
|
|
2731
|
-
const src =
|
|
2732
|
-
const dest =
|
|
2533
|
+
const src = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
2534
|
+
const dest = join7(rulesDir, name);
|
|
2733
2535
|
copyFileSync2(src, dest);
|
|
2734
2536
|
}
|
|
2735
2537
|
return names;
|
|
@@ -2738,8 +2540,8 @@ function syncBaseSkills(skillsDir) {
|
|
|
2738
2540
|
mkdirSync4(skillsDir, { recursive: true });
|
|
2739
2541
|
const names = listBaseSkillDirNames();
|
|
2740
2542
|
for (const name of names) {
|
|
2741
|
-
const src =
|
|
2742
|
-
const dest =
|
|
2543
|
+
const src = join7(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
2544
|
+
const dest = join7(skillsDir, name);
|
|
2743
2545
|
cpSync(src, dest, { recursive: true, force: true });
|
|
2744
2546
|
}
|
|
2745
2547
|
return names;
|
|
@@ -2756,26 +2558,26 @@ function syncSupplementarySkills(skillsDir, list) {
|
|
|
2756
2558
|
skipped.push(dirName);
|
|
2757
2559
|
continue;
|
|
2758
2560
|
}
|
|
2759
|
-
const skillDir =
|
|
2561
|
+
const skillDir = join7(skillsDir, dirName);
|
|
2760
2562
|
mkdirSync4(skillDir, { recursive: true });
|
|
2761
|
-
|
|
2563
|
+
writeFileSync6(join7(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
|
|
2762
2564
|
written.push(dirName);
|
|
2763
2565
|
}
|
|
2764
2566
|
const removed = [];
|
|
2765
|
-
if (!
|
|
2766
|
-
for (const entry of
|
|
2767
|
-
const full =
|
|
2567
|
+
if (!existsSync5(skillsDir)) return { written, skipped, removed };
|
|
2568
|
+
for (const entry of readdirSync3(skillsDir)) {
|
|
2569
|
+
const full = join7(skillsDir, entry);
|
|
2768
2570
|
if (!statSync3(full).isDirectory()) continue;
|
|
2769
2571
|
if (baseNames.has(entry)) continue;
|
|
2770
2572
|
if (apiDirNames.has(entry)) continue;
|
|
2771
|
-
|
|
2573
|
+
rmSync(full, { recursive: true, force: true });
|
|
2772
2574
|
removed.push(entry);
|
|
2773
2575
|
}
|
|
2774
2576
|
return { written, skipped, removed };
|
|
2775
2577
|
}
|
|
2776
2578
|
|
|
2777
2579
|
// src/rules-sync.ts
|
|
2778
|
-
var
|
|
2580
|
+
var MANIFEST_FILE2 = ".rules-sync-manifest.json";
|
|
2779
2581
|
function ruleLocalFileName(ruleName) {
|
|
2780
2582
|
const trimmed = ruleName.trim();
|
|
2781
2583
|
if (!trimmed) return "rule.md";
|
|
@@ -2784,13 +2586,13 @@ function ruleLocalFileName(ruleName) {
|
|
|
2784
2586
|
return `${sanitized}.md`;
|
|
2785
2587
|
}
|
|
2786
2588
|
function loadManifest2(rulesDir) {
|
|
2787
|
-
const path19 =
|
|
2788
|
-
if (!
|
|
2589
|
+
const path19 = join8(rulesDir, MANIFEST_FILE2);
|
|
2590
|
+
if (!existsSync6(toFsPath(path19))) {
|
|
2789
2591
|
return { version: 1, rules: {} };
|
|
2790
2592
|
}
|
|
2791
2593
|
try {
|
|
2792
2594
|
const parsed = JSON.parse(
|
|
2793
|
-
|
|
2595
|
+
readFileSync5(toFsPath(path19), "utf8")
|
|
2794
2596
|
);
|
|
2795
2597
|
if (parsed?.version === 1 && parsed.rules && typeof parsed.rules === "object") {
|
|
2796
2598
|
return parsed;
|
|
@@ -2800,8 +2602,8 @@ function loadManifest2(rulesDir) {
|
|
|
2800
2602
|
return { version: 1, rules: {} };
|
|
2801
2603
|
}
|
|
2802
2604
|
function saveManifest2(rulesDir, manifest) {
|
|
2803
|
-
|
|
2804
|
-
toFsPath(
|
|
2605
|
+
writeFileSync7(
|
|
2606
|
+
toFsPath(join8(rulesDir, MANIFEST_FILE2)),
|
|
2805
2607
|
`${JSON.stringify(manifest, null, 2)}
|
|
2806
2608
|
`,
|
|
2807
2609
|
"utf8"
|
|
@@ -2811,18 +2613,18 @@ function isBaseRuleFileName(fileName) {
|
|
|
2811
2613
|
return listBaseRuleFileNames().includes(basename3(fileName));
|
|
2812
2614
|
}
|
|
2813
2615
|
function isRuleUpToDate(entry, rule, dest) {
|
|
2814
|
-
if (!entry || !
|
|
2616
|
+
if (!entry || !existsSync6(toFsPath(dest))) return false;
|
|
2815
2617
|
if (entry.fileName !== ruleLocalFileName(rule.name)) return false;
|
|
2816
2618
|
const updatedAt = rule.updatedAt ?? "";
|
|
2817
2619
|
if (entry.updatedAt !== updatedAt) return false;
|
|
2818
|
-
const localContent =
|
|
2620
|
+
const localContent = readFileSync5(toFsPath(dest), "utf8");
|
|
2819
2621
|
return localContent === (rule.content ?? "");
|
|
2820
2622
|
}
|
|
2821
2623
|
async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
2822
2624
|
const api = createApmApiClient(cfg);
|
|
2823
2625
|
const baseline = await resolveBranchBaseline(api, sessionId, workdirPath);
|
|
2824
2626
|
const repositoryId = baseline.repositoryId;
|
|
2825
|
-
const rulesDir =
|
|
2627
|
+
const rulesDir = join8(apmRoot ?? workspaceApmDir(workdirPath), "rules");
|
|
2826
2628
|
await ensureDirExists(rulesDir);
|
|
2827
2629
|
if (!repositoryId) {
|
|
2828
2630
|
console.log(
|
|
@@ -2839,7 +2641,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
|
2839
2641
|
for (const rule of list) {
|
|
2840
2642
|
remoteIds.add(rule.id);
|
|
2841
2643
|
const fileName = ruleLocalFileName(rule.name);
|
|
2842
|
-
const dest =
|
|
2644
|
+
const dest = join8(rulesDir, fileName);
|
|
2843
2645
|
const entry = manifest.rules[rule.id];
|
|
2844
2646
|
const updatedAt = rule.updatedAt ?? "";
|
|
2845
2647
|
if (isRuleUpToDate(entry, rule, dest)) {
|
|
@@ -2848,7 +2650,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
|
2848
2650
|
console.log(`[apm] \u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: rules/${fileName}`);
|
|
2849
2651
|
continue;
|
|
2850
2652
|
}
|
|
2851
|
-
|
|
2653
|
+
writeFileSync7(toFsPath(dest), rule.content ?? "", "utf8");
|
|
2852
2654
|
nextManifest.rules[rule.id] = { fileName, updatedAt };
|
|
2853
2655
|
written.push(fileName);
|
|
2854
2656
|
console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
|
|
@@ -2857,9 +2659,9 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
|
2857
2659
|
for (const [ruleId, entry] of Object.entries(manifest.rules)) {
|
|
2858
2660
|
if (remoteIds.has(ruleId)) continue;
|
|
2859
2661
|
if (isBaseRuleFileName(entry.fileName)) continue;
|
|
2860
|
-
const dest =
|
|
2861
|
-
if (
|
|
2862
|
-
|
|
2662
|
+
const dest = join8(rulesDir, entry.fileName);
|
|
2663
|
+
if (existsSync6(toFsPath(dest))) {
|
|
2664
|
+
rmSync2(toFsPath(dest), { force: true });
|
|
2863
2665
|
}
|
|
2864
2666
|
removed.push(entry.fileName);
|
|
2865
2667
|
console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u5E73\u53F0\u89C4\u5219: rules/${entry.fileName}`);
|
|
@@ -2868,6 +2670,275 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
|
|
|
2868
2670
|
return { written, skipped, removed, repositoryId };
|
|
2869
2671
|
}
|
|
2870
2672
|
|
|
2673
|
+
// src/project-documents-sync.ts
|
|
2674
|
+
init_client();
|
|
2675
|
+
init_config();
|
|
2676
|
+
import {
|
|
2677
|
+
existsSync as existsSync7,
|
|
2678
|
+
readdirSync as readdirSync4,
|
|
2679
|
+
readFileSync as readFileSync6,
|
|
2680
|
+
rmSync as rmSync3,
|
|
2681
|
+
writeFileSync as writeFileSync8
|
|
2682
|
+
} from "fs";
|
|
2683
|
+
import { createHash } from "crypto";
|
|
2684
|
+
import { dirname as dirname3, join as join9, relative as relative2, sep } from "path";
|
|
2685
|
+
var MANIFEST_FILE3 = "manifest.json";
|
|
2686
|
+
function normalizeProjectIdForPath(projectId) {
|
|
2687
|
+
const id = projectId.trim();
|
|
2688
|
+
if (!id) {
|
|
2689
|
+
throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2690
|
+
}
|
|
2691
|
+
if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
|
|
2692
|
+
throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
|
|
2693
|
+
}
|
|
2694
|
+
return id;
|
|
2695
|
+
}
|
|
2696
|
+
function projectDocumentsDir(apmRoot, projectId) {
|
|
2697
|
+
const id = normalizeProjectIdForPath(projectId);
|
|
2698
|
+
return join9(apmRoot ?? workspaceApmDir(), "project", id);
|
|
2699
|
+
}
|
|
2700
|
+
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
2701
|
+
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
2702
|
+
return join9(
|
|
2703
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2704
|
+
...normalized.split("/")
|
|
2705
|
+
);
|
|
2706
|
+
}
|
|
2707
|
+
function normalizeLocalDocumentPath(path19) {
|
|
2708
|
+
const trimmed = path19.trim().replace(/\\/g, "/");
|
|
2709
|
+
if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
|
|
2710
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path19}`);
|
|
2711
|
+
}
|
|
2712
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
2713
|
+
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
2714
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path19}`);
|
|
2715
|
+
}
|
|
2716
|
+
return segments.join("/");
|
|
2717
|
+
}
|
|
2718
|
+
function hashLocalFileContent(content) {
|
|
2719
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
2720
|
+
}
|
|
2721
|
+
function readLocalManifest(apmRoot, projectId) {
|
|
2722
|
+
const manifestPath3 = join9(
|
|
2723
|
+
projectDocumentsDir(apmRoot, projectId),
|
|
2724
|
+
MANIFEST_FILE3
|
|
2725
|
+
);
|
|
2726
|
+
if (!existsSync7(manifestPath3)) {
|
|
2727
|
+
return null;
|
|
2728
|
+
}
|
|
2729
|
+
try {
|
|
2730
|
+
return JSON.parse(
|
|
2731
|
+
readFileSync6(manifestPath3, "utf8")
|
|
2732
|
+
);
|
|
2733
|
+
} catch {
|
|
2734
|
+
return null;
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
2738
|
+
const root = projectDocumentsDir(apmRoot, projectId);
|
|
2739
|
+
if (!existsSync7(root)) {
|
|
2740
|
+
return [];
|
|
2741
|
+
}
|
|
2742
|
+
const paths = [];
|
|
2743
|
+
const walk = (dir) => {
|
|
2744
|
+
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
2745
|
+
const abs = join9(dir, entry.name);
|
|
2746
|
+
if (entry.isDirectory()) {
|
|
2747
|
+
walk(abs);
|
|
2748
|
+
continue;
|
|
2749
|
+
}
|
|
2750
|
+
if (entry.isFile() && entry.name === MANIFEST_FILE3) {
|
|
2751
|
+
continue;
|
|
2752
|
+
}
|
|
2753
|
+
const rel = relative2(root, abs).split(sep).join("/");
|
|
2754
|
+
paths.push(rel);
|
|
2755
|
+
}
|
|
2756
|
+
};
|
|
2757
|
+
walk(root);
|
|
2758
|
+
return paths.sort();
|
|
2759
|
+
}
|
|
2760
|
+
function diffManifestPaths(remote, local) {
|
|
2761
|
+
const remoteMap = new Map(
|
|
2762
|
+
(remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2763
|
+
);
|
|
2764
|
+
const localMap = new Map(
|
|
2765
|
+
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2766
|
+
);
|
|
2767
|
+
const download = [];
|
|
2768
|
+
for (const [path19, hash] of remoteMap) {
|
|
2769
|
+
if (localMap.get(path19) !== hash) {
|
|
2770
|
+
download.push(path19);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
const deleteLocal = [];
|
|
2774
|
+
for (const path19 of localMap.keys()) {
|
|
2775
|
+
if (!remoteMap.has(path19)) {
|
|
2776
|
+
deleteLocal.push(path19);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
return { download, deleteLocal };
|
|
2780
|
+
}
|
|
2781
|
+
async function resolveProjectIdForSync(api, workdirPath, options) {
|
|
2782
|
+
const explicit = options?.projectId?.trim();
|
|
2783
|
+
if (explicit) {
|
|
2784
|
+
return { projectId: explicit, diagnostic: null };
|
|
2785
|
+
}
|
|
2786
|
+
try {
|
|
2787
|
+
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
2788
|
+
const projectId = baseline.projectId?.trim() || null;
|
|
2789
|
+
if (projectId) {
|
|
2790
|
+
return { projectId, diagnostic: null };
|
|
2791
|
+
}
|
|
2792
|
+
const projectIds = baseline.projectIds ?? [];
|
|
2793
|
+
if (projectIds.length === 0) {
|
|
2794
|
+
return {
|
|
2795
|
+
projectId: null,
|
|
2796
|
+
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
|
|
2797
|
+
\u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
return {
|
|
2801
|
+
projectId: null,
|
|
2802
|
+
diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
|
|
2803
|
+
\u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
|
|
2804
|
+
};
|
|
2805
|
+
} catch (err) {
|
|
2806
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
2807
|
+
return {
|
|
2808
|
+
projectId: null,
|
|
2809
|
+
diagnostic: detail.replace(/^\[apm\]\s*/, "")
|
|
2810
|
+
};
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
|
|
2814
|
+
const empty = {
|
|
2815
|
+
synced: false,
|
|
2816
|
+
projectId: null,
|
|
2817
|
+
downloaded: 0,
|
|
2818
|
+
deleted: 0
|
|
2819
|
+
};
|
|
2820
|
+
const cfg = await tryReadApmConfig();
|
|
2821
|
+
if (!cfg || !resolveApiKey(cfg)) {
|
|
2822
|
+
console.log(
|
|
2823
|
+
"[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"
|
|
2824
|
+
);
|
|
2825
|
+
return empty;
|
|
2826
|
+
}
|
|
2827
|
+
const api = createApmApiClient(cfg);
|
|
2828
|
+
const { projectId, diagnostic } = await resolveProjectIdForSync(
|
|
2829
|
+
api,
|
|
2830
|
+
workdirPath,
|
|
2831
|
+
options
|
|
2832
|
+
);
|
|
2833
|
+
if (!projectId) {
|
|
2834
|
+
console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
|
|
2835
|
+
${diagnostic ?? ""}`);
|
|
2836
|
+
return empty;
|
|
2837
|
+
}
|
|
2838
|
+
const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
|
|
2839
|
+
const docsDir = projectDocumentsDir(targetApmDir, projectId);
|
|
2840
|
+
await ensureDirExists(docsDir);
|
|
2841
|
+
const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
|
|
2842
|
+
{ projectId }
|
|
2843
|
+
);
|
|
2844
|
+
if (!remoteManifest) {
|
|
2845
|
+
console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
|
|
2846
|
+
return { ...empty, projectId };
|
|
2847
|
+
}
|
|
2848
|
+
const localManifest = readLocalManifest(targetApmDir, projectId);
|
|
2849
|
+
const { download, deleteLocal } = diffManifestPaths(
|
|
2850
|
+
remoteManifest,
|
|
2851
|
+
localManifest
|
|
2852
|
+
);
|
|
2853
|
+
let downloaded = 0;
|
|
2854
|
+
if (download.length > 0) {
|
|
2855
|
+
const { list } = await api.cli.listProjectDocuments({
|
|
2856
|
+
projectId,
|
|
2857
|
+
paths: download.join(",")
|
|
2858
|
+
});
|
|
2859
|
+
for (const doc of list) {
|
|
2860
|
+
const absPath = toFsPath(
|
|
2861
|
+
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
2862
|
+
);
|
|
2863
|
+
await ensureDirExists(dirname3(absPath));
|
|
2864
|
+
writeFileSync8(absPath, doc.content, "utf8");
|
|
2865
|
+
downloaded += 1;
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
let deleted = 0;
|
|
2869
|
+
for (const path19 of deleteLocal) {
|
|
2870
|
+
const absPath = toFsPath(
|
|
2871
|
+
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
2872
|
+
);
|
|
2873
|
+
if (existsSync7(absPath)) {
|
|
2874
|
+
rmSync3(absPath, { force: true });
|
|
2875
|
+
deleted += 1;
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
writeFileSync8(
|
|
2879
|
+
toFsPath(join9(docsDir, MANIFEST_FILE3)),
|
|
2880
|
+
`${JSON.stringify(remoteManifest, null, 2)}
|
|
2881
|
+
`,
|
|
2882
|
+
"utf8"
|
|
2883
|
+
);
|
|
2884
|
+
console.log(
|
|
2885
|
+
`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
|
|
2886
|
+
);
|
|
2887
|
+
return {
|
|
2888
|
+
synced: true,
|
|
2889
|
+
projectId,
|
|
2890
|
+
downloaded,
|
|
2891
|
+
deleted
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2894
|
+
async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
2895
|
+
const api = createApmApiClient(cfg);
|
|
2896
|
+
const { projectId } = await resolveProjectIdForSync(
|
|
2897
|
+
api,
|
|
2898
|
+
workdirPath,
|
|
2899
|
+
options
|
|
2900
|
+
);
|
|
2901
|
+
if (!projectId) {
|
|
2902
|
+
return 0;
|
|
2903
|
+
}
|
|
2904
|
+
const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
|
|
2905
|
+
const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
|
|
2906
|
+
if (localPaths.length === 0) {
|
|
2907
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2908
|
+
return 0;
|
|
2909
|
+
}
|
|
2910
|
+
const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
|
|
2911
|
+
const remoteHashByPath = new Map(
|
|
2912
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
2913
|
+
);
|
|
2914
|
+
const remoteDescriptionByPath = new Map(
|
|
2915
|
+
(remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
|
|
2916
|
+
);
|
|
2917
|
+
let synced = 0;
|
|
2918
|
+
for (const path19 of localPaths) {
|
|
2919
|
+
const absPath = toFsPath(
|
|
2920
|
+
projectDocumentLocalPath(targetApmDir, projectId, path19)
|
|
2921
|
+
);
|
|
2922
|
+
const content = readFileSync6(absPath, "utf8");
|
|
2923
|
+
const contentHash = hashLocalFileContent(content);
|
|
2924
|
+
if (remoteHashByPath.get(path19) === contentHash) {
|
|
2925
|
+
continue;
|
|
2926
|
+
}
|
|
2927
|
+
await api.cli.upsertProjectDocument({
|
|
2928
|
+
projectId,
|
|
2929
|
+
path: path19,
|
|
2930
|
+
content,
|
|
2931
|
+
description: remoteDescriptionByPath.get(path19) ?? void 0
|
|
2932
|
+
});
|
|
2933
|
+
synced += 1;
|
|
2934
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path19}`);
|
|
2935
|
+
}
|
|
2936
|
+
if (synced === 0) {
|
|
2937
|
+
console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
2938
|
+
}
|
|
2939
|
+
return synced;
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2871
2942
|
// src/commands/pull.ts
|
|
2872
2943
|
async function runPull(sessionId, remoteWorkdir) {
|
|
2873
2944
|
const trimmedId = sessionId.trim();
|
|
@@ -10557,10 +10628,11 @@ function buildProgram() {
|
|
|
10557
10628
|
).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) => {
|
|
10558
10629
|
await runLogin(opts);
|
|
10559
10630
|
});
|
|
10560
|
-
program.command("init").description(
|
|
10561
|
-
"
|
|
10562
|
-
|
|
10563
|
-
|
|
10631
|
+
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").option(
|
|
10632
|
+
"--webide",
|
|
10633
|
+
"\u6309 WebIDE \u5DE5\u4F5C\u533A\u521D\u59CB\u5316\uFF1A\u4EC5 webide \u89C4\u5219\u4E0E\u6307\u5357\uFF0C\u4E0D\u542B sessions / skills / deploy"
|
|
10634
|
+
).action(async (opts) => {
|
|
10635
|
+
await runInit(opts.name, { webide: opts.webide === true });
|
|
10564
10636
|
});
|
|
10565
10637
|
program.command("update").description(
|
|
10566
10638
|
`\u901A\u8FC7 npm \u5168\u5C40\u5B89\u88C5 ${CLI_PACKAGE_NAME}\uFF0C\u9ED8\u8BA4\u66F4\u65B0\u5230\u5F53\u524D\u5927\u7248\u672C\uFF08${parseMajorVersion(
|