ai-project-manage-cli 6.0.26 → 6.0.28
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 +102 -28
- package/package.json +1 -1
- package/template/rules/write_doc.md +4 -2
package/dist/index.js
CHANGED
|
@@ -525,13 +525,10 @@ function formatSessionMessagesXml(sessionId, messages) {
|
|
|
525
525
|
import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
526
526
|
import { join as join4 } from "path";
|
|
527
527
|
var MANIFEST_FILE = ".sync-manifest.json";
|
|
528
|
-
async function downloadAttachment(cfg,
|
|
528
|
+
async function downloadAttachment(cfg, attachmentId) {
|
|
529
529
|
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
530
|
-
const
|
|
531
|
-
const
|
|
532
|
-
const res = await fetch(url, {
|
|
533
|
-
headers: { Authorization: `Bearer ${cfg.token}` }
|
|
534
|
-
});
|
|
530
|
+
const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({ attachmentId })}`;
|
|
531
|
+
const res = await fetch(url);
|
|
535
532
|
if (!res.ok) {
|
|
536
533
|
throw new Error(
|
|
537
534
|
`[apm] \u4E0B\u8F7D\u9644\u4EF6\u5931\u8D25 (${res.status}): attachmentId=${attachmentId}`
|
|
@@ -589,7 +586,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
|
|
|
589
586
|
);
|
|
590
587
|
continue;
|
|
591
588
|
}
|
|
592
|
-
const buffer = await downloadAttachment(cfg,
|
|
589
|
+
const buffer = await downloadAttachment(cfg, item.id);
|
|
593
590
|
writeFileSync3(dest, buffer);
|
|
594
591
|
nextManifest.attachments[item.id] = {
|
|
595
592
|
name: item.name,
|
|
@@ -888,7 +885,73 @@ async function runUpdateSkills() {
|
|
|
888
885
|
}
|
|
889
886
|
|
|
890
887
|
// src/commands/sync-document.ts
|
|
891
|
-
import { existsSync as
|
|
888
|
+
import { existsSync as existsSync6 } from "fs";
|
|
889
|
+
import { basename as basename2 } from "path";
|
|
890
|
+
|
|
891
|
+
// src/commands/sync-session-documents.ts
|
|
892
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "fs";
|
|
893
|
+
import { join as join9 } from "path";
|
|
894
|
+
function listLocalMarkdownFiles(docsDir) {
|
|
895
|
+
if (!existsSync5(docsDir)) {
|
|
896
|
+
return [];
|
|
897
|
+
}
|
|
898
|
+
return readdirSync3(docsDir).filter(
|
|
899
|
+
(name) => name.toLowerCase().endsWith(".md")
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
function remoteDocumentByLocalName(remoteDocuments, localFileName) {
|
|
903
|
+
const platformName = documentPlatformName(localFileName);
|
|
904
|
+
return remoteDocuments.find((doc) => {
|
|
905
|
+
const remoteLocalName = documentLocalFileName(doc.name);
|
|
906
|
+
return remoteLocalName === localFileName || documentPlatformName(doc.name) === platformName;
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
async function upsertLocalDocumentFile(api, sessionId, docsDir, fileName) {
|
|
910
|
+
const absPath = join9(docsDir, fileName);
|
|
911
|
+
const content = readFileSync5(absPath, "utf8");
|
|
912
|
+
const name = documentPlatformName(absPath);
|
|
913
|
+
return api.cli.upsertDocument({
|
|
914
|
+
sessionId,
|
|
915
|
+
name,
|
|
916
|
+
content
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
async function syncSessionDocuments(cfg, sessionId, apmRoot, options) {
|
|
920
|
+
const trimmedSessionId = sessionId.trim();
|
|
921
|
+
if (!trimmedSessionId) {
|
|
922
|
+
return 0;
|
|
923
|
+
}
|
|
924
|
+
const docsDir = sessionDocsDir(trimmedSessionId, apmRoot);
|
|
925
|
+
const localFiles = listLocalMarkdownFiles(docsDir);
|
|
926
|
+
if (localFiles.length === 0) {
|
|
927
|
+
return 0;
|
|
928
|
+
}
|
|
929
|
+
const api = options?.api ?? createApmApiClient(cfg);
|
|
930
|
+
const remoteDocuments = options?.remoteDocuments ?? await api.cli.listDocuments({ sessionId: trimmedSessionId });
|
|
931
|
+
let synced = 0;
|
|
932
|
+
for (const fileName of localFiles) {
|
|
933
|
+
const absPath = join9(docsDir, fileName);
|
|
934
|
+
const content = readFileSync5(absPath, "utf8");
|
|
935
|
+
const remote = remoteDocumentByLocalName(remoteDocuments, fileName);
|
|
936
|
+
if (remote && remote.content === content) {
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
const doc = await upsertLocalDocumentFile(
|
|
940
|
+
api,
|
|
941
|
+
trimmedSessionId,
|
|
942
|
+
docsDir,
|
|
943
|
+
fileName
|
|
944
|
+
);
|
|
945
|
+
synced += 1;
|
|
946
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u6587\u6863: ${doc.name} (id=${doc.id})`);
|
|
947
|
+
}
|
|
948
|
+
if (synced === 0) {
|
|
949
|
+
console.log("[apm] \u4F1A\u8BDD\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
950
|
+
}
|
|
951
|
+
return synced;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// src/commands/sync-document.ts
|
|
892
955
|
async function runSyncDocument(sessionId, options) {
|
|
893
956
|
const trimmedSessionId = sessionId.trim();
|
|
894
957
|
if (!trimmedSessionId) {
|
|
@@ -901,24 +964,24 @@ async function runSyncDocument(sessionId, options) {
|
|
|
901
964
|
process.exit(1);
|
|
902
965
|
}
|
|
903
966
|
const absPath = resolveSessionDocumentPath(trimmedSessionId, fileArg);
|
|
904
|
-
if (!
|
|
905
|
-
const
|
|
967
|
+
if (!existsSync6(absPath)) {
|
|
968
|
+
const docsDir2 = sessionDocsDir(trimmedSessionId);
|
|
906
969
|
console.error(
|
|
907
970
|
`[apm] \u6587\u6863\u4E0D\u5B58\u5728: ${absPath}
|
|
908
|
-
[apm] \u8BF7\u786E\u8BA4\u5DF2 pull\uFF0C\u4E14 ${
|
|
971
|
+
[apm] \u8BF7\u786E\u8BA4\u5DF2 pull\uFF0C\u4E14 ${docsDir2} \u4E0B\u5B58\u5728\u5BF9\u5E94\u6587\u4EF6`
|
|
909
972
|
);
|
|
910
973
|
process.exit(1);
|
|
911
974
|
}
|
|
912
|
-
const content = readFileSync5(absPath, "utf8");
|
|
913
|
-
const name = documentPlatformName(absPath);
|
|
914
975
|
const cfg = await ensureLoggedConfig();
|
|
915
976
|
const api = createApmApiClient(cfg);
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
977
|
+
const docsDir = sessionDocsDir(trimmedSessionId);
|
|
978
|
+
const doc = await upsertLocalDocumentFile(
|
|
979
|
+
api,
|
|
980
|
+
trimmedSessionId,
|
|
981
|
+
docsDir,
|
|
982
|
+
basename2(absPath)
|
|
983
|
+
);
|
|
984
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u6587\u6863: ${doc.name} (id=${doc.id})`);
|
|
922
985
|
}
|
|
923
986
|
|
|
924
987
|
// src/commands/append-message.ts
|
|
@@ -1041,6 +1104,7 @@ function validateAgentWsMessage(value, kind) {
|
|
|
1041
1104
|
|
|
1042
1105
|
// src/commands/connect/cursor-agent.ts
|
|
1043
1106
|
import { Agent, CursorAgentError } from "@cursor/sdk";
|
|
1107
|
+
import { setMaxListeners } from "node:events";
|
|
1044
1108
|
import { resolve as resolve3 } from "path";
|
|
1045
1109
|
|
|
1046
1110
|
// src/session-utils.ts
|
|
@@ -1288,6 +1352,7 @@ async function syncCursorMessageLog(cfg, ctx, events) {
|
|
|
1288
1352
|
}
|
|
1289
1353
|
|
|
1290
1354
|
// src/commands/connect/cursor-agent.ts
|
|
1355
|
+
setMaxListeners(50);
|
|
1291
1356
|
var logCtx = (ctx, agentId) => ({
|
|
1292
1357
|
sessionId: ctx.sessionId,
|
|
1293
1358
|
messageId: ctx.messageId,
|
|
@@ -1396,6 +1461,15 @@ async function handleInboundMessage(cfg, raw) {
|
|
|
1396
1461
|
apiKey: msg.apiKey,
|
|
1397
1462
|
workdir: msg.workdir
|
|
1398
1463
|
});
|
|
1464
|
+
await syncSessionDocuments(
|
|
1465
|
+
cfg,
|
|
1466
|
+
msg.sessionId,
|
|
1467
|
+
workspaceApmDir(msg.workdir)
|
|
1468
|
+
);
|
|
1469
|
+
await commitWorkingTreeIfDirty(
|
|
1470
|
+
msg.workdir,
|
|
1471
|
+
"chore(apm): sync session documents"
|
|
1472
|
+
);
|
|
1399
1473
|
await updateMessageStatus(cfg, messageId, "SUCCESS");
|
|
1400
1474
|
} catch (err) {
|
|
1401
1475
|
console.error(
|
|
@@ -1492,14 +1566,14 @@ async function runConnect(options) {
|
|
|
1492
1566
|
import path5 from "node:path";
|
|
1493
1567
|
|
|
1494
1568
|
// src/commands/deploy/internal/apm-config.ts
|
|
1495
|
-
import { existsSync as
|
|
1569
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "node:fs";
|
|
1496
1570
|
import { resolve as resolve4 } from "node:path";
|
|
1497
1571
|
function loadApmConfig(options) {
|
|
1498
1572
|
const p = resolve4(
|
|
1499
1573
|
process.cwd(),
|
|
1500
1574
|
options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
|
|
1501
1575
|
);
|
|
1502
|
-
if (!
|
|
1576
|
+
if (!existsSync7(p)) {
|
|
1503
1577
|
console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
|
|
1504
1578
|
process.exit(1);
|
|
1505
1579
|
}
|
|
@@ -1626,7 +1700,7 @@ import path4 from "node:path";
|
|
|
1626
1700
|
import Docker from "dockerode";
|
|
1627
1701
|
|
|
1628
1702
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
|
|
1629
|
-
import { existsSync as
|
|
1703
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
|
|
1630
1704
|
import path from "node:path";
|
|
1631
1705
|
function asOptionalTlsBuffer(value) {
|
|
1632
1706
|
if (typeof value !== "string") {
|
|
@@ -1638,7 +1712,7 @@ function asOptionalTlsBuffer(value) {
|
|
|
1638
1712
|
if (normalized === "") {
|
|
1639
1713
|
return void 0;
|
|
1640
1714
|
}
|
|
1641
|
-
if (
|
|
1715
|
+
if (existsSync8(normalized)) {
|
|
1642
1716
|
return readFileSync7(normalized);
|
|
1643
1717
|
}
|
|
1644
1718
|
const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
|
|
@@ -1849,7 +1923,7 @@ var DockerodeClient = class {
|
|
|
1849
1923
|
var createDockerodeClient = (config) => new DockerodeClient(config);
|
|
1850
1924
|
|
|
1851
1925
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
|
|
1852
|
-
import { existsSync as
|
|
1926
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
|
|
1853
1927
|
import path2 from "node:path";
|
|
1854
1928
|
function stripSurroundingQuotes(value) {
|
|
1855
1929
|
const t = value.trim();
|
|
@@ -1866,7 +1940,7 @@ function loadEnvFromFile(envFilePath) {
|
|
|
1866
1940
|
return {};
|
|
1867
1941
|
}
|
|
1868
1942
|
const targetPath = path2.resolve(envFilePath);
|
|
1869
|
-
if (!
|
|
1943
|
+
if (!existsSync9(targetPath) || !statSync4(targetPath).isFile()) {
|
|
1870
1944
|
return {};
|
|
1871
1945
|
}
|
|
1872
1946
|
const raw = readFileSync8(targetPath, "utf-8");
|
|
@@ -2040,12 +2114,12 @@ function dockerPushImage(params, cwd) {
|
|
|
2040
2114
|
}
|
|
2041
2115
|
|
|
2042
2116
|
// src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
|
|
2043
|
-
import { existsSync as
|
|
2117
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
2044
2118
|
import path3 from "node:path";
|
|
2045
2119
|
function resolveDockerBuildPaths(cwd) {
|
|
2046
2120
|
const dockerfilePath = path3.join(cwd, "Dockerfile");
|
|
2047
2121
|
Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
|
|
2048
|
-
if (!
|
|
2122
|
+
if (!existsSync10(dockerfilePath)) {
|
|
2049
2123
|
throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
|
|
2050
2124
|
}
|
|
2051
2125
|
Logger.info("\u2713 Dockerfile \u5B58\u5728");
|
|
@@ -2646,7 +2720,7 @@ function registerDeployCommands(program) {
|
|
|
2646
2720
|
function buildProgram() {
|
|
2647
2721
|
const program = new Command();
|
|
2648
2722
|
program.name("apm").description(
|
|
2649
|
-
|
|
2723
|
+
`\u6BD4\u90BB\u661F\u56FE\u547D\u4EE4\u884C\uFF08\u4F1A\u8BDD\u5DE5\u4F5C\u533A\u4E0E\u7814\u53D1\u81EA\u52A8\u5316\uFF09\u3002
|
|
2650
2724
|
\u672A\u4F20 --server \u65F6\u4F18\u5148\u4F7F\u7528\u73AF\u5883\u53D8\u91CF AI_PM_SERVER\uFF0C\u5426\u5219\u9ED8\u8BA4 ${DEFAULT_BASE_URL}\u3002`
|
|
2651
2725
|
).version(readCliVersion(), "-V, --version", "\u663E\u793A\u7248\u672C\u53F7").helpOption("-h, --help", "\u663E\u793A\u5E2E\u52A9").showHelpAfterError(true);
|
|
2652
2726
|
program.command("login").description(
|
package/package.json
CHANGED
|
@@ -18,9 +18,11 @@
|
|
|
18
18
|
|
|
19
19
|
## 在保存完成之后需要同步到远程
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
通过 `apm connect` 连接平台时,每轮 Agent 结束后会自动推送 `docs/` 下变更的 Markdown,一般无需手动同步。
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
手动同步命令: `apm sync-document <会话 ID> --file=<文档名称>`
|
|
24
|
+
|
|
25
|
+
示例: `apm sync-document <会话ID> --file=张三-工作日志.md`
|
|
24
26
|
|
|
25
27
|
## 注意事项
|
|
26
28
|
|