ai-project-manage-cli 6.0.44-alpha.1 → 6.0.45

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.
Files changed (2) hide show
  1. package/dist/index.js +217 -109
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ function resolveClientMachineId(cfg) {
16
16
  function resolveApiKey(cfg) {
17
17
  return (cfg.apiKey ?? cfg.token ?? "").trim();
18
18
  }
19
- async function readApmConfig() {
19
+ async function tryReadApmConfig() {
20
20
  try {
21
21
  const raw = readFileSync(APM_CONFIG_PATH, "utf8");
22
22
  const v = JSON.parse(raw);
@@ -54,7 +54,7 @@ function httpBaseToWsOrigin(httpBase) {
54
54
  throw new Error("baseUrl \u5FC5\u987B\u4EE5 http:// \u6216 https:// \u5F00\u5934");
55
55
  }
56
56
  async function ensureApmConfig() {
57
- const existing = await readApmConfig();
57
+ const existing = await tryReadApmConfig();
58
58
  if (existing) return existing;
59
59
  const defaults = defaultApmConfig();
60
60
  await writeApmConfig(defaults);
@@ -80,8 +80,8 @@ function buildAgentWsUrl(httpBase, apiKey) {
80
80
  }
81
81
 
82
82
  // src/commands/init.ts
83
- import { join as join3 } from "path";
84
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
83
+ import { join as join4 } from "path";
84
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
85
85
 
86
86
  // src/command-utils.ts
87
87
  import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
@@ -287,32 +287,9 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
287
287
  assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
288
288
  }
289
289
 
290
- // src/commands/init.ts
291
- async function runInit(name) {
292
- const workdir = resolveWorkdirPath();
293
- await ensureWorkspaceApmDirForInit(workdir);
294
- const apmDir = workspaceApmDir(workdir);
295
- await copyTemplateFiles(apmDir, workdir);
296
- const trimmedName = name?.trim();
297
- if (trimmedName) {
298
- const apmConfigPath = toFsPath(join3(apmDir, "apm.config.json"));
299
- const config = readFileSync2(apmConfigPath, "utf8");
300
- const configJson = JSON.parse(config);
301
- configJson.name = trimmedName;
302
- writeFileSync2(
303
- apmConfigPath,
304
- `${JSON.stringify(configJson, null, 2)}
305
- `,
306
- "utf8"
307
- );
308
- }
309
- console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
310
- console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
311
- console.log("[apm] \u8BF7\u5728\u5E73\u53F0\u300C\u5BA2\u6237\u673A\u7BA1\u7406 \u2192 \u5DE5\u4F5C\u7A7A\u95F4\u300D\u767B\u8BB0\u4E0A\u8FF0\u76EE\u5F55\u8DEF\u5F84");
312
- }
313
-
314
- // src/commands/login.ts
315
- import { ApiError } from "listpage-http";
290
+ // src/deployment-config-sync.ts
291
+ import { join as join3 } from "path";
292
+ import { writeFileSync as writeFileSync2 } from "fs";
316
293
 
317
294
  // src/api/client.ts
318
295
  import { createApiClient } from "listpage-http";
@@ -373,6 +350,18 @@ var requestConfig = {
373
350
  method: "GET",
374
351
  path: "/cli/tasks/branch-baseline"
375
352
  }),
353
+ workspaceBaseline: defineEndpoint({
354
+ method: "GET",
355
+ path: "/cli/workspaces/baseline"
356
+ }),
357
+ getDeploymentConfiguration: defineEndpoint({
358
+ method: "GET",
359
+ path: "/cli/deployment-configurations"
360
+ }),
361
+ matchRepository: defineEndpoint({
362
+ method: "GET",
363
+ path: "/cli/repositories/match"
364
+ }),
376
365
  listSkills: defineEndpoint({
377
366
  method: "GET",
378
367
  path: "/cli/skills"
@@ -395,7 +384,94 @@ function createApmApiClient(cfg) {
395
384
  });
396
385
  }
397
386
 
387
+ // src/deployment-config-sync.ts
388
+ var SYNC_HINT = "\u767B\u8BB0\u5DE5\u4F5C\u7A7A\u95F4\u8DEF\u5F84\u3001\u7ED1\u5B9A\u4ED3\u5E93\u540E\uFF0C\u53EF\u6267\u884C: apm sync-deploy-config";
389
+ async function syncRemoteDeploymentConfig(workdirPath, apmDir) {
390
+ const cfg = await tryReadApmConfig();
391
+ if (!cfg || !resolveApiKey(cfg)) {
392
+ console.log(
393
+ "[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E\u540C\u6B65\uFF08\u4FDD\u7559\u6A21\u677F apm.config.json\uFF09\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\uFF0C\u518D\u6267\u884C apm sync-deploy-config \u62C9\u53D6\u6700\u65B0\u914D\u7F6E\u3002"
394
+ );
395
+ return { synced: false, repositoryId: null };
396
+ }
397
+ const api = createApmApiClient(cfg);
398
+ const baseline = await api.cli.workspaceBaseline({ workdirPath });
399
+ const repositoryId = baseline.repositoryId;
400
+ if (!repositoryId) {
401
+ const detail = baseline.diagnostic?.message ?? `\u5F53\u524D\u8DEF\u5F84\uFF08\u89C4\u8303\u5316\uFF1A${baseline.workdirPath}\uFF09\u672A\u5339\u914D\u5230\u5DF2\u7ED1\u5B9A\u4ED3\u5E93\u7684\u5DE5\u4F5C\u7A7A\u95F4\u3002`;
402
+ console.log(
403
+ `[apm] \u672A\u80FD\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E\uFF0C\u4FDD\u7559\u6A21\u677F apm.config.json\u3002
404
+ ${detail}
405
+ [apm] ${SYNC_HINT}`
406
+ );
407
+ return { synced: false, repositoryId: null };
408
+ }
409
+ const { config } = await api.cli.getDeploymentConfiguration({ repositoryId });
410
+ if (!config) {
411
+ console.log(
412
+ `[apm] \u672A\u627E\u5230\u5173\u8054\u4ED3\u5E93\u7684\u90E8\u7F72\u914D\u7F6E\uFF0C\u4FDD\u7559\u6A21\u677F apm.config.json\uFF08repositoryId\uFF1A${repositoryId}\uFF09\u3002
413
+ [apm] \u8BF7\u5728\u5E73\u53F0\u300C\u90E8\u7F72\u914D\u7F6E\u300D\u4E2D\u521B\u5EFA\u914D\u7F6E\u5E76\u5173\u8054\u8BE5\u4ED3\u5E93\uFF0C\u7136\u540E\u6267\u884C: apm sync-deploy-config`
414
+ );
415
+ return { synced: false, repositoryId };
416
+ }
417
+ let parsed;
418
+ try {
419
+ parsed = JSON.parse(config.content);
420
+ } catch {
421
+ console.warn(
422
+ `[apm] \u8FDC\u7A0B\u90E8\u7F72\u914D\u7F6E\u300C${config.name}\u300DJSON \u65E0\u6548\uFF0C\u4F7F\u7528\u6A21\u677F\u90E8\u7F72\u914D\u7F6E`
423
+ );
424
+ return { synced: false, repositoryId };
425
+ }
426
+ const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
427
+ const apmConfigPath = toFsPath(join3(targetApmDir, "apm.config.json"));
428
+ writeFileSync2(apmConfigPath, `${JSON.stringify(parsed, null, 2)}
429
+ `, "utf8");
430
+ const deployDir = join3(targetApmDir, "deploy");
431
+ await ensureDirExists(deployDir);
432
+ writeFileSync2(
433
+ toFsPath(join3(deployDir, "README.md")),
434
+ config.deploymentDoc ?? "",
435
+ "utf8"
436
+ );
437
+ console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E: ${config.name}`);
438
+ console.log("[apm] \u5DF2\u5199\u5165 .apm/apm.config.json \u4E0E .apm/deploy/README.md");
439
+ return { synced: true, repositoryId, configName: config.name };
440
+ }
441
+
442
+ // src/commands/init.ts
443
+ async function runInit(name) {
444
+ const workdir = resolveWorkdirPath();
445
+ await ensureWorkspaceApmDirForInit(workdir);
446
+ const apmDir = workspaceApmDir(workdir);
447
+ await copyTemplateFiles(apmDir, workdir);
448
+ const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
449
+ const trimmedName = name?.trim();
450
+ if (trimmedName) {
451
+ const apmConfigPath = toFsPath(join4(apmDir, "apm.config.json"));
452
+ const config = readFileSync2(apmConfigPath, "utf8");
453
+ const configJson = JSON.parse(config);
454
+ configJson.name = trimmedName;
455
+ writeFileSync3(
456
+ apmConfigPath,
457
+ `${JSON.stringify(configJson, null, 2)}
458
+ `,
459
+ "utf8"
460
+ );
461
+ }
462
+ console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
463
+ console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
464
+ if (!syncResult.synced) {
465
+ console.log(
466
+ "[apm] \u5F53\u524D apm.config.json \u4E3A\u6A21\u677F\u9ED8\u8BA4\u503C\uFF1B\u5B8C\u6210\u5E73\u53F0\u767B\u8BB0\u540E\u6267\u884C apm sync-deploy-config \u62C9\u53D6\u90E8\u7F72\u914D\u7F6E"
467
+ );
468
+ }
469
+ 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");
470
+ }
471
+
398
472
  // src/commands/login.ts
473
+ import { existsSync as existsSync2 } from "fs";
474
+ import { ApiError } from "listpage-http";
399
475
  async function runLogin(opts) {
400
476
  const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
401
477
  const apiKey = (opts.apiKey?.trim() || process.env.APM_API_KEY?.trim() || "").replace(/^Bearer\s+/i, "");
@@ -447,6 +523,11 @@ async function runLogin(opts) {
447
523
  2
448
524
  )
449
525
  );
526
+ const workdir = resolveWorkdirPath();
527
+ const apmDir = workspaceApmDir(workdir);
528
+ if (existsSync2(apmDir)) {
529
+ await syncRemoteDeploymentConfig(workdir, apmDir);
530
+ }
450
531
  }
451
532
 
452
533
  // src/commands/branch.ts
@@ -617,8 +698,8 @@ async function runBranch(sessionId, options = {}) {
617
698
  }
618
699
 
619
700
  // src/commands/pull.ts
620
- import { writeFileSync as writeFileSync6 } from "fs";
621
- import { join as join7 } from "path";
701
+ import { writeFileSync as writeFileSync7 } from "fs";
702
+ import { join as join8 } from "path";
622
703
  import { stringify as yamlStringify } from "yaml";
623
704
 
624
705
  // src/session-messages-xml.ts
@@ -651,8 +732,8 @@ function formatSessionMessagesXml(sessionId, messages) {
651
732
  }
652
733
 
653
734
  // src/commands/sync-session-attachments.ts
654
- import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
655
- import { join as join4 } from "path";
735
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
736
+ import { join as join5 } from "path";
656
737
  var MANIFEST_FILE = ".sync-manifest.json";
657
738
  async function downloadAttachment(cfg, attachmentId) {
658
739
  const base = cfg.baseUrl.trim().replace(/\/+$/, "");
@@ -666,8 +747,8 @@ async function downloadAttachment(cfg, attachmentId) {
666
747
  return Buffer.from(await res.arrayBuffer());
667
748
  }
668
749
  function loadManifest(dir) {
669
- const path10 = join4(dir, MANIFEST_FILE);
670
- if (!existsSync2(path10)) {
750
+ const path10 = join5(dir, MANIFEST_FILE);
751
+ if (!existsSync3(path10)) {
671
752
  return { version: 1, attachments: {} };
672
753
  }
673
754
  try {
@@ -682,21 +763,21 @@ function loadManifest(dir) {
682
763
  return { version: 1, attachments: {} };
683
764
  }
684
765
  function saveManifest(dir, manifest) {
685
- writeFileSync3(
686
- join4(dir, MANIFEST_FILE),
766
+ writeFileSync4(
767
+ join5(dir, MANIFEST_FILE),
687
768
  `${JSON.stringify(manifest, null, 2)}
688
769
  `,
689
770
  "utf8"
690
771
  );
691
772
  }
692
773
  function isAttachmentUpToDate(entry, item, dest) {
693
- if (!entry || !existsSync2(dest)) return false;
774
+ if (!entry || !existsSync3(dest)) return false;
694
775
  if (entry.name !== item.name) return false;
695
776
  const createdAt = item.createdAt ?? "";
696
777
  return entry.createdAt === createdAt;
697
778
  }
698
779
  async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
699
- const dir = join4(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
780
+ const dir = join5(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
700
781
  await ensureDirExists(dir);
701
782
  if (attachments.length === 0) {
702
783
  saveManifest(dir, { version: 1, attachments: {} });
@@ -705,7 +786,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
705
786
  const manifest = loadManifest(dir);
706
787
  const nextManifest = { version: 1, attachments: {} };
707
788
  for (const item of attachments) {
708
- const dest = join4(dir, item.name);
789
+ const dest = join5(dir, item.name);
709
790
  const entry = manifest.attachments[item.id];
710
791
  const createdAt = item.createdAt ?? "";
711
792
  if (isAttachmentUpToDate(entry, item, dest)) {
@@ -716,7 +797,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
716
797
  continue;
717
798
  }
718
799
  const buffer = await downloadAttachment(cfg, item.id);
719
- writeFileSync3(dest, buffer);
800
+ writeFileSync4(dest, buffer);
720
801
  nextManifest.attachments[item.id] = {
721
802
  name: item.name,
722
803
  createdAt
@@ -727,46 +808,46 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
727
808
  }
728
809
 
729
810
  // src/rules-sync.ts
730
- import { basename as basename2, extname as extname2, join as join6 } from "path";
731
- import { existsSync as existsSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
811
+ import { basename as basename2, extname as extname2, join as join7 } from "path";
812
+ import { existsSync as existsSync5, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
732
813
 
733
814
  // src/skills-sync.ts
734
815
  import {
735
816
  copyFileSync as copyFileSync2,
736
817
  cpSync,
737
- existsSync as existsSync3,
818
+ existsSync as existsSync4,
738
819
  mkdirSync as mkdirSync3,
739
820
  readdirSync as readdirSync2,
740
821
  rmSync,
741
822
  statSync as statSync2,
742
- writeFileSync as writeFileSync4
823
+ writeFileSync as writeFileSync5
743
824
  } from "fs";
744
- import { join as join5 } from "path";
745
- var AGENTS_TEMPLATE_PATH = join5(CLI_TEMPLATE_DIR, "AGENTS.md");
746
- var BASE_SKILLS_TEMPLATE_DIR = join5(CLI_TEMPLATE_DIR, "skills");
747
- var BASE_RULES_TEMPLATE_DIR = join5(CLI_TEMPLATE_DIR, "rules");
825
+ import { join as join6 } from "path";
826
+ var AGENTS_TEMPLATE_PATH = join6(CLI_TEMPLATE_DIR, "AGENTS.md");
827
+ var BASE_SKILLS_TEMPLATE_DIR = join6(CLI_TEMPLATE_DIR, "skills");
828
+ var BASE_RULES_TEMPLATE_DIR = join6(CLI_TEMPLATE_DIR, "rules");
748
829
  function sanitizeSkillDirName(name) {
749
830
  const trimmed = name.trim();
750
831
  if (!trimmed) return "skill";
751
832
  return trimmed.replace(/[/\\:*?"<>|]/g, "_");
752
833
  }
753
834
  function listBaseSkillDirNames() {
754
- if (!existsSync3(BASE_SKILLS_TEMPLATE_DIR)) return [];
835
+ if (!existsSync4(BASE_SKILLS_TEMPLATE_DIR)) return [];
755
836
  return readdirSync2(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
756
- const path10 = join5(BASE_SKILLS_TEMPLATE_DIR, name);
837
+ const path10 = join6(BASE_SKILLS_TEMPLATE_DIR, name);
757
838
  return statSync2(path10).isDirectory();
758
839
  });
759
840
  }
760
841
  function syncAgentsGuide(apmDir) {
761
- if (!existsSync3(AGENTS_TEMPLATE_PATH)) return false;
842
+ if (!existsSync4(AGENTS_TEMPLATE_PATH)) return false;
762
843
  mkdirSync3(apmDir, { recursive: true });
763
- copyFileSync2(AGENTS_TEMPLATE_PATH, join5(apmDir, "AGENTS.md"));
844
+ copyFileSync2(AGENTS_TEMPLATE_PATH, join6(apmDir, "AGENTS.md"));
764
845
  return true;
765
846
  }
766
847
  function listBaseRuleFileNames() {
767
- if (!existsSync3(BASE_RULES_TEMPLATE_DIR)) return [];
848
+ if (!existsSync4(BASE_RULES_TEMPLATE_DIR)) return [];
768
849
  return readdirSync2(BASE_RULES_TEMPLATE_DIR).filter((name) => {
769
- const path10 = join5(BASE_RULES_TEMPLATE_DIR, name);
850
+ const path10 = join6(BASE_RULES_TEMPLATE_DIR, name);
770
851
  return statSync2(path10).isFile();
771
852
  });
772
853
  }
@@ -774,8 +855,8 @@ function syncBaseRules(rulesDir) {
774
855
  mkdirSync3(rulesDir, { recursive: true });
775
856
  const names = listBaseRuleFileNames();
776
857
  for (const name of names) {
777
- const src = join5(BASE_RULES_TEMPLATE_DIR, name);
778
- const dest = join5(rulesDir, name);
858
+ const src = join6(BASE_RULES_TEMPLATE_DIR, name);
859
+ const dest = join6(rulesDir, name);
779
860
  copyFileSync2(src, dest);
780
861
  }
781
862
  return names;
@@ -784,8 +865,8 @@ function syncBaseSkills(skillsDir) {
784
865
  mkdirSync3(skillsDir, { recursive: true });
785
866
  const names = listBaseSkillDirNames();
786
867
  for (const name of names) {
787
- const src = join5(BASE_SKILLS_TEMPLATE_DIR, name);
788
- const dest = join5(skillsDir, name);
868
+ const src = join6(BASE_SKILLS_TEMPLATE_DIR, name);
869
+ const dest = join6(skillsDir, name);
789
870
  cpSync(src, dest, { recursive: true, force: true });
790
871
  }
791
872
  return names;
@@ -802,15 +883,15 @@ function syncSupplementarySkills(skillsDir, list) {
802
883
  skipped.push(dirName);
803
884
  continue;
804
885
  }
805
- const skillDir = join5(skillsDir, dirName);
886
+ const skillDir = join6(skillsDir, dirName);
806
887
  mkdirSync3(skillDir, { recursive: true });
807
- writeFileSync4(join5(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
888
+ writeFileSync5(join6(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
808
889
  written.push(dirName);
809
890
  }
810
891
  const removed = [];
811
- if (!existsSync3(skillsDir)) return { written, skipped, removed };
892
+ if (!existsSync4(skillsDir)) return { written, skipped, removed };
812
893
  for (const entry of readdirSync2(skillsDir)) {
813
- const full = join5(skillsDir, entry);
894
+ const full = join6(skillsDir, entry);
814
895
  if (!statSync2(full).isDirectory()) continue;
815
896
  if (baseNames.has(entry)) continue;
816
897
  if (apiDirNames.has(entry)) continue;
@@ -830,8 +911,8 @@ function ruleLocalFileName(ruleName) {
830
911
  return `${sanitized}.md`;
831
912
  }
832
913
  function loadManifest2(rulesDir) {
833
- const path10 = join6(rulesDir, MANIFEST_FILE2);
834
- if (!existsSync4(toFsPath(path10))) {
914
+ const path10 = join7(rulesDir, MANIFEST_FILE2);
915
+ if (!existsSync5(toFsPath(path10))) {
835
916
  return { version: 1, rules: {} };
836
917
  }
837
918
  try {
@@ -846,8 +927,8 @@ function loadManifest2(rulesDir) {
846
927
  return { version: 1, rules: {} };
847
928
  }
848
929
  function saveManifest2(rulesDir, manifest) {
849
- writeFileSync5(
850
- toFsPath(join6(rulesDir, MANIFEST_FILE2)),
930
+ writeFileSync6(
931
+ toFsPath(join7(rulesDir, MANIFEST_FILE2)),
851
932
  `${JSON.stringify(manifest, null, 2)}
852
933
  `,
853
934
  "utf8"
@@ -857,7 +938,7 @@ function isBaseRuleFileName(fileName) {
857
938
  return listBaseRuleFileNames().includes(basename2(fileName));
858
939
  }
859
940
  function isRuleUpToDate(entry, rule, dest) {
860
- if (!entry || !existsSync4(toFsPath(dest))) return false;
941
+ if (!entry || !existsSync5(toFsPath(dest))) return false;
861
942
  if (entry.fileName !== ruleLocalFileName(rule.name)) return false;
862
943
  const updatedAt = rule.updatedAt ?? "";
863
944
  if (entry.updatedAt !== updatedAt) return false;
@@ -868,7 +949,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
868
949
  const api = createApmApiClient(cfg);
869
950
  const baseline = await api.cli.branchBaseline({ sessionId, workdirPath });
870
951
  const repositoryId = baseline.repositoryId;
871
- const rulesDir = join6(apmRoot ?? workspaceApmDir(workdirPath), "rules");
952
+ const rulesDir = join7(apmRoot ?? workspaceApmDir(workdirPath), "rules");
872
953
  await ensureDirExists(rulesDir);
873
954
  if (!repositoryId) {
874
955
  console.log(
@@ -885,7 +966,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
885
966
  for (const rule of list) {
886
967
  remoteIds.add(rule.id);
887
968
  const fileName = ruleLocalFileName(rule.name);
888
- const dest = join6(rulesDir, fileName);
969
+ const dest = join7(rulesDir, fileName);
889
970
  const entry = manifest.rules[rule.id];
890
971
  const updatedAt = rule.updatedAt ?? "";
891
972
  if (isRuleUpToDate(entry, rule, dest)) {
@@ -894,7 +975,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
894
975
  console.log(`[apm] \u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: rules/${fileName}`);
895
976
  continue;
896
977
  }
897
- writeFileSync5(toFsPath(dest), rule.content ?? "", "utf8");
978
+ writeFileSync6(toFsPath(dest), rule.content ?? "", "utf8");
898
979
  nextManifest.rules[rule.id] = { fileName, updatedAt };
899
980
  written.push(fileName);
900
981
  console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
@@ -903,8 +984,8 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
903
984
  for (const [ruleId, entry] of Object.entries(manifest.rules)) {
904
985
  if (remoteIds.has(ruleId)) continue;
905
986
  if (isBaseRuleFileName(entry.fileName)) continue;
906
- const dest = join6(rulesDir, entry.fileName);
907
- if (existsSync4(toFsPath(dest))) {
987
+ const dest = join7(rulesDir, entry.fileName);
988
+ if (existsSync5(toFsPath(dest))) {
908
989
  rmSync2(toFsPath(dest), { force: true });
909
990
  }
910
991
  removed.push(entry.fileName);
@@ -937,20 +1018,20 @@ async function runPull(sessionId, remoteWorkdir) {
937
1018
  const dir = sessionDir(trimmedId, apmRoot);
938
1019
  const docsDir = sessionDocsDir(trimmedId, apmRoot);
939
1020
  await ensureDirExists(docsDir);
940
- writeFileSync6(
1021
+ writeFileSync7(
941
1022
  sessionRulePath(trimmedId, apmRoot),
942
1023
  detail.description ?? "",
943
1024
  "utf8"
944
1025
  );
945
- writeFileSync6(
1026
+ writeFileSync7(
946
1027
  sessionTaskPath(trimmedId, apmRoot),
947
1028
  detail.task.description ?? "",
948
1029
  "utf8"
949
1030
  );
950
- writeFileSync6(sessionTodoPath(trimmedId, apmRoot), detail.todo ?? "", "utf8");
1031
+ writeFileSync7(sessionTodoPath(trimmedId, apmRoot), detail.todo ?? "", "utf8");
951
1032
  for (const doc of documents) {
952
1033
  const fileName = documentLocalFileName(doc.name);
953
- writeFileSync6(join7(docsDir, fileName), doc.content ?? "", "utf8");
1034
+ writeFileSync7(join8(docsDir, fileName), doc.content ?? "", "utf8");
954
1035
  }
955
1036
  const sessionYaml = yamlStringify(
956
1037
  {
@@ -967,13 +1048,13 @@ async function runPull(sessionId, remoteWorkdir) {
967
1048
  },
968
1049
  { lineWidth: 0 }
969
1050
  );
970
- writeFileSync6(
1051
+ writeFileSync7(
971
1052
  sessionYamlPath(trimmedId, apmRoot),
972
1053
  sessionYaml.endsWith("\n") ? sessionYaml : `${sessionYaml}
973
1054
  `,
974
1055
  "utf8"
975
1056
  );
976
- writeFileSync6(
1057
+ writeFileSync7(
977
1058
  sessionMessagesXmlPath(trimmedId, apmRoot),
978
1059
  formatSessionMessagesXml(trimmedId, messages),
979
1060
  "utf8"
@@ -989,13 +1070,13 @@ import { spawnSync } from "child_process";
989
1070
 
990
1071
  // src/version.ts
991
1072
  import { readFileSync as readFileSync5 } from "fs";
992
- import { dirname as dirname2, join as join8 } from "path";
1073
+ import { dirname as dirname2, join as join9 } from "path";
993
1074
  import { fileURLToPath as fileURLToPath2 } from "url";
994
1075
  var CLI_PACKAGE_NAME = "ai-project-manage-cli";
995
1076
  function readCliVersion() {
996
1077
  try {
997
1078
  const dir = dirname2(fileURLToPath2(import.meta.url));
998
- const pkgPath = join8(dir, "..", "package.json");
1079
+ const pkgPath = join9(dir, "..", "package.json");
999
1080
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
1000
1081
  return pkg.version ?? "0.0.0";
1001
1082
  } catch {
@@ -1063,11 +1144,11 @@ async function runUpdate() {
1063
1144
  }
1064
1145
 
1065
1146
  // src/commands/update-skills.ts
1066
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
1067
- import { join as join9 } from "path";
1147
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
1148
+ import { join as join10 } from "path";
1068
1149
  async function runUpdateSkills() {
1069
1150
  const apmDir = workspaceApmDir();
1070
- if (!existsSync5(apmDir)) {
1151
+ if (!existsSync6(apmDir)) {
1071
1152
  console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
1072
1153
  process.exit(1);
1073
1154
  }
@@ -1081,12 +1162,12 @@ async function runUpdateSkills() {
1081
1162
  if (syncAgentsGuide(apmDir)) {
1082
1163
  console.log("[apm] \u5DF2\u540C\u6B65 APM \u6307\u5357: .apm/AGENTS.md");
1083
1164
  }
1084
- const rulesDir = join9(apmDir, "rules");
1165
+ const rulesDir = join10(apmDir, "rules");
1085
1166
  const ruleNames = syncBaseRules(rulesDir);
1086
1167
  for (const name of ruleNames) {
1087
1168
  console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u89C4\u5219: rules/${name}`);
1088
1169
  }
1089
- const skillsDir = join9(apmDir, "skills");
1170
+ const skillsDir = join10(apmDir, "skills");
1090
1171
  mkdirSync4(skillsDir, { recursive: true });
1091
1172
  const baseNames = syncBaseSkills(skillsDir);
1092
1173
  for (const name of baseNames) {
@@ -1112,15 +1193,35 @@ async function runUpdateSkills() {
1112
1193
  );
1113
1194
  }
1114
1195
 
1196
+ // src/commands/sync-deploy-config.ts
1197
+ import { existsSync as existsSync7, statSync as statSync4 } from "fs";
1198
+ async function runSyncDeployConfig() {
1199
+ const workdir = resolveWorkdirPath();
1200
+ const apmDir = workspaceApmDir(workdir);
1201
+ if (!existsSync7(apmDir)) {
1202
+ console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
1203
+ process.exit(1);
1204
+ }
1205
+ const apmStat = statSync4(apmDir);
1206
+ if (!apmStat.isDirectory()) {
1207
+ throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${apmDir}`);
1208
+ }
1209
+ await ensureLoggedConfig();
1210
+ const result = await syncRemoteDeploymentConfig(workdir, apmDir);
1211
+ if (!result.synced) {
1212
+ process.exit(1);
1213
+ }
1214
+ }
1215
+
1115
1216
  // src/commands/sync-document.ts
1116
- import { existsSync as existsSync7 } from "fs";
1217
+ import { existsSync as existsSync9 } from "fs";
1117
1218
  import { basename as basename3 } from "path";
1118
1219
 
1119
1220
  // src/commands/sync-session-documents.ts
1120
- import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
1121
- import { join as join10 } from "path";
1221
+ import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
1222
+ import { join as join11 } from "path";
1122
1223
  function listLocalMarkdownFiles(docsDir) {
1123
- if (!existsSync6(docsDir)) {
1224
+ if (!existsSync8(docsDir)) {
1124
1225
  return [];
1125
1226
  }
1126
1227
  return readdirSync3(docsDir).filter(
@@ -1135,7 +1236,7 @@ function remoteDocumentByLocalName(remoteDocuments, localFileName) {
1135
1236
  });
1136
1237
  }
1137
1238
  async function upsertLocalDocumentFile(api, sessionId, docsDir, fileName) {
1138
- const absPath = join10(docsDir, fileName);
1239
+ const absPath = join11(docsDir, fileName);
1139
1240
  const content = readFileSync6(absPath, "utf8");
1140
1241
  const name = documentPlatformName(absPath);
1141
1242
  return api.cli.upsertDocument({
@@ -1158,7 +1259,7 @@ async function syncSessionDocuments(cfg, sessionId, apmRoot, options) {
1158
1259
  const remoteDocuments = options?.remoteDocuments ?? await api.cli.listDocuments({ sessionId: trimmedSessionId });
1159
1260
  let synced = 0;
1160
1261
  for (const fileName of localFiles) {
1161
- const absPath = join10(docsDir, fileName);
1262
+ const absPath = join11(docsDir, fileName);
1162
1263
  const content = readFileSync6(absPath, "utf8");
1163
1264
  const remote = remoteDocumentByLocalName(remoteDocuments, fileName);
1164
1265
  if (remote && remote.content === content) {
@@ -1192,7 +1293,7 @@ async function runSyncDocument(sessionId, options) {
1192
1293
  process.exit(1);
1193
1294
  }
1194
1295
  const absPath = resolveSessionDocumentPath(trimmedSessionId, fileArg);
1195
- if (!existsSync7(absPath)) {
1296
+ if (!existsSync9(absPath)) {
1196
1297
  const docsDir2 = sessionDocsDir(trimmedSessionId);
1197
1298
  console.error(
1198
1299
  `[apm] \u6587\u6863\u4E0D\u5B58\u5728: ${absPath}
@@ -1592,13 +1693,13 @@ ${JSON.stringify(event, null, 2)}
1592
1693
  }
1593
1694
 
1594
1695
  // src/commands/connect/agent-session-registry.ts
1595
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
1696
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "node:fs";
1596
1697
  import { dirname as dirname3, resolve as resolve3 } from "node:path";
1597
1698
  function registryPath(workdir, sessionId) {
1598
1699
  return resolve3(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
1599
1700
  }
1600
1701
  function readRegistry(path10) {
1601
- if (!existsSync8(path10)) {
1702
+ if (!existsSync10(path10)) {
1602
1703
  return {};
1603
1704
  }
1604
1705
  try {
@@ -1620,7 +1721,7 @@ function readRegistry(path10) {
1620
1721
  }
1621
1722
  function writeRegistry(path10, registry) {
1622
1723
  mkdirSync5(dirname3(path10), { recursive: true });
1623
- writeFileSync7(path10, `${JSON.stringify(registry, null, 2)}
1724
+ writeFileSync8(path10, `${JSON.stringify(registry, null, 2)}
1624
1725
  `, "utf8");
1625
1726
  }
1626
1727
  function loadSessionAgentId(workdir, sessionId, user) {
@@ -2238,14 +2339,14 @@ async function runConnect(options) {
2238
2339
  import path5 from "node:path";
2239
2340
 
2240
2341
  // src/commands/deploy/internal/apm-config.ts
2241
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
2342
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
2242
2343
  import { resolve as resolve4 } from "node:path";
2243
2344
  function loadApmConfig(options) {
2244
2345
  const p = resolve4(
2245
2346
  process.cwd(),
2246
2347
  options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
2247
2348
  );
2248
- if (!existsSync9(p)) {
2349
+ if (!existsSync11(p)) {
2249
2350
  console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
2250
2351
  process.exit(1);
2251
2352
  }
@@ -2372,7 +2473,7 @@ import path4 from "node:path";
2372
2473
  import Docker from "dockerode";
2373
2474
 
2374
2475
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
2375
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "node:fs";
2476
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
2376
2477
  import path from "node:path";
2377
2478
  function asOptionalTlsBuffer(value) {
2378
2479
  if (typeof value !== "string") {
@@ -2384,7 +2485,7 @@ function asOptionalTlsBuffer(value) {
2384
2485
  if (normalized === "") {
2385
2486
  return void 0;
2386
2487
  }
2387
- if (existsSync10(normalized)) {
2488
+ if (existsSync12(normalized)) {
2388
2489
  return readFileSync9(normalized);
2389
2490
  }
2390
2491
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
@@ -2595,7 +2696,7 @@ var DockerodeClient = class {
2595
2696
  var createDockerodeClient = (config) => new DockerodeClient(config);
2596
2697
 
2597
2698
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
2598
- import { existsSync as existsSync11, readFileSync as readFileSync10, statSync as statSync4 } from "node:fs";
2699
+ import { existsSync as existsSync13, readFileSync as readFileSync10, statSync as statSync5 } from "node:fs";
2599
2700
  import path2 from "node:path";
2600
2701
  function stripSurroundingQuotes(value) {
2601
2702
  const t = value.trim();
@@ -2612,7 +2713,7 @@ function loadEnvFromFile(envFilePath) {
2612
2713
  return {};
2613
2714
  }
2614
2715
  const targetPath = path2.resolve(envFilePath);
2615
- if (!existsSync11(targetPath) || !statSync4(targetPath).isFile()) {
2716
+ if (!existsSync13(targetPath) || !statSync5(targetPath).isFile()) {
2616
2717
  return {};
2617
2718
  }
2618
2719
  const raw = readFileSync10(targetPath, "utf-8");
@@ -2786,12 +2887,12 @@ function dockerPushImage(params, cwd) {
2786
2887
  }
2787
2888
 
2788
2889
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
2789
- import { existsSync as existsSync12 } from "node:fs";
2890
+ import { existsSync as existsSync14 } from "node:fs";
2790
2891
  import path3 from "node:path";
2791
2892
  function resolveDockerBuildPaths(cwd) {
2792
2893
  const dockerfilePath = path3.join(cwd, "Dockerfile");
2793
2894
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
2794
- if (!existsSync12(dockerfilePath)) {
2895
+ if (!existsSync14(dockerfilePath)) {
2795
2896
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
2796
2897
  }
2797
2898
  Logger.info("\u2713 Dockerfile \u5B58\u5728");
@@ -2920,14 +3021,14 @@ import { copyFile, readdir as readdir2, stat } from "node:fs/promises";
2920
3021
  import path7 from "node:path";
2921
3022
 
2922
3023
  // src/commands/deploy/internal/minio.ts
2923
- import { statSync as statSync5 } from "node:fs";
3024
+ import { statSync as statSync6 } from "node:fs";
2924
3025
  import { readdir, readFile } from "node:fs/promises";
2925
3026
  import path6 from "node:path";
2926
3027
  import * as Minio from "minio";
2927
3028
  var DEFAULT_MAX_FILE_SIZE_MB = 50;
2928
3029
  async function isDirectoryPath(dir) {
2929
3030
  try {
2930
- const st = statSync5(dir);
3031
+ const st = statSync6(dir);
2931
3032
  return st.isDirectory();
2932
3033
  } catch {
2933
3034
  return false;
@@ -2957,7 +3058,7 @@ async function collectFiles(root) {
2957
3058
  if (e.isDirectory()) {
2958
3059
  await walk(abs, rel);
2959
3060
  } else if (e.isFile()) {
2960
- const st = statSync5(abs);
3061
+ const st = statSync6(abs);
2961
3062
  out.push({
2962
3063
  absPath: abs,
2963
3064
  relativePath: rel.replace(/\\/g, "/"),
@@ -3400,7 +3501,9 @@ function buildProgram() {
3400
3501
  ).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) => {
3401
3502
  await runLogin(opts);
3402
3503
  });
3403
- program.command("init").description("\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").option("--name <name>", "\u5DE5\u4F5C\u76EE\u5F55\u540D\u79F0").action(async (opts) => {
3504
+ program.command("init").description(
3505
+ "\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 \u4E0E deploy/README.md"
3506
+ ).option("--name <name>", "\u5DE5\u4F5C\u76EE\u5F55\u540D\u79F0").action(async (opts) => {
3404
3507
  await runInit(opts.name);
3405
3508
  });
3406
3509
  program.command("update").description(
@@ -3413,6 +3516,11 @@ function buildProgram() {
3413
3516
  ).action(async () => {
3414
3517
  await runUpdateSkills();
3415
3518
  });
3519
+ program.command("sync-deploy-config").description(
3520
+ "\u4ECE\u5E73\u53F0\u62C9\u53D6\u90E8\u7F72\u914D\u7F6E\uFF0C\u8986\u76D6 .apm/apm.config.json \u4E0E .apm/deploy/README.md\uFF08\u9700\u5DF2 login \u4E14\u5DE5\u4F5C\u7A7A\u95F4\u5DF2\u767B\u8BB0\u5E76\u7ED1\u5B9A\u4ED3\u5E93\uFF09"
3521
+ ).action(async () => {
3522
+ await runSyncDeployConfig();
3523
+ });
3416
3524
  program.command("pull").description(
3417
3525
  "\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF08session.yaml\u3001RULE.md\u3001TASK.md\u3001TODO.md\u3001docs\u3001attachments\uFF09"
3418
3526
  ).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.44-alpha.1",
3
+ "version": "6.0.45",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,