glab-agent 0.2.3 → 0.2.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glab-agent",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "type": "module",
5
5
  "description": "Multi-agent GitLab To-Do watcher with YAML-defined agents, skills, and GitLab registry.",
6
6
  "license": "MIT",
@@ -91,10 +91,10 @@ export interface GitlabClient {
91
91
  updateUserStatus(emoji: string, message: string, availability?: "busy" | "not_set"): Promise<void>;
92
92
  updateUserBio(bio: string): Promise<void>;
93
93
  listAllPendingTodos(): Promise<GitlabTodoItem[]>;
94
- listWikiPages(projectId: number): Promise<WikiPage[]>;
95
- getWikiPage(projectId: number, slug: string): Promise<WikiPage>;
96
- createWikiPage(projectId: number, title: string, content: string): Promise<WikiPage>;
97
- updateWikiPage(projectId: number, slug: string, title: string, content: string): Promise<WikiPage>;
94
+ listWikiPages(projectId: number | string): Promise<WikiPage[]>;
95
+ getWikiPage(projectId: number | string, slug: string): Promise<WikiPage>;
96
+ createWikiPage(projectId: number | string, title: string, content: string): Promise<WikiPage>;
97
+ updateWikiPage(projectId: number | string, slug: string, title: string, content: string): Promise<WikiPage>;
98
98
  }
99
99
 
100
100
  interface GitlabGlabClientOptions {
@@ -632,7 +632,7 @@ export class GitlabGlabClient implements GitlabClient {
632
632
  });
633
633
  }
634
634
 
635
- async listWikiPages(projectId: number): Promise<WikiPage[]> {
635
+ async listWikiPages(projectId: number | string): Promise<WikiPage[]> {
636
636
  const endpoint = this.withQuery(`projects/${projectId}/wikis`, { with_content: "0" });
637
637
  let payload: unknown;
638
638
  try {
@@ -656,7 +656,7 @@ export class GitlabGlabClient implements GitlabClient {
656
656
  .filter((w): w is WikiPage => w !== undefined);
657
657
  }
658
658
 
659
- async getWikiPage(projectId: number, slug: string): Promise<WikiPage> {
659
+ async getWikiPage(projectId: number | string, slug: string): Promise<WikiPage> {
660
660
  const payload = await this.readJson(`projects/${projectId}/wikis/${encodeURIComponent(slug)}`);
661
661
  const p = (payload ?? {}) as Payload;
662
662
  return {
@@ -666,7 +666,7 @@ export class GitlabGlabClient implements GitlabClient {
666
666
  };
667
667
  }
668
668
 
669
- async createWikiPage(projectId: number, title: string, content: string): Promise<WikiPage> {
669
+ async createWikiPage(projectId: number | string, title: string, content: string): Promise<WikiPage> {
670
670
  const stdout = await this.request(`projects/${projectId}/wikis`, {
671
671
  method: "POST",
672
672
  fields: { title, content }
@@ -679,7 +679,7 @@ export class GitlabGlabClient implements GitlabClient {
679
679
  };
680
680
  }
681
681
 
682
- async updateWikiPage(projectId: number, slug: string, title: string, content: string): Promise<WikiPage> {
682
+ async updateWikiPage(projectId: number | string, slug: string, title: string, content: string): Promise<WikiPage> {
683
683
  // title must be passed to preserve directory prefix in slug (GitLab API quirk)
684
684
  const stdout = await this.request(`projects/${projectId}/wikis/${encodeURIComponent(slug)}`, {
685
685
  method: "PUT",
@@ -797,6 +797,91 @@ async function updateAgentUserBio(
797
797
  }
798
798
  }
799
799
 
800
+ async function publishAgentProfileWiki(
801
+ config: LocalAgentConfig,
802
+ dependencies: WatcherDependencies
803
+ ): Promise<void> {
804
+ const logger = dependencies.logger ?? console;
805
+ const def = config.agentDefinition;
806
+ if (!def) return;
807
+
808
+ // Resolve project identifier: explicit/inferred ID, or encoded path from git remote
809
+ let projectRef: number | string | undefined = config.gitlabProjectId;
810
+ if (!projectRef) {
811
+ const remoteUrl = readOriginRemote(config.agentRepoPath, execFileSync);
812
+ const remote = remoteUrl ? parseGitRemoteUrl(remoteUrl) : undefined;
813
+ if (remote) {
814
+ // GitLab API accepts URL-encoded project path as project ID
815
+ const projectPath = remote.projectPath.startsWith("git/")
816
+ ? remote.projectPath.replace(/^git\//, "")
817
+ : remote.projectPath;
818
+ projectRef = encodeURIComponent(projectPath);
819
+ }
820
+ }
821
+ if (!projectRef) return;
822
+
823
+ const agentName = def.name;
824
+ const slug = `agents/${agentName}`;
825
+
826
+ // Build profile page content
827
+ const lines: string[] = [
828
+ `# ${def.display_name || agentName}`,
829
+ "",
830
+ ];
831
+
832
+ if (def.description) {
833
+ lines.push(def.description, "");
834
+ }
835
+
836
+ // Skills section — skill_refs are the string names from YAML; fall back to parsed skill objects
837
+ const skillNames = def.skill_refs.length > 0
838
+ ? def.skill_refs
839
+ : def.skills.map(s => s.name);
840
+ if (skillNames.length > 0) {
841
+ lines.push("## Skills", "", ...skillNames.map(s => `- ${s}`), "");
842
+ }
843
+
844
+ // Triggers section
845
+ const triggers = def.triggers;
846
+ if (triggers.labels.length > 0) {
847
+ lines.push("## Trigger Labels", "", ...triggers.labels.map(l => `- \`${l}\``), "");
848
+ }
849
+
850
+ // Instructions preview (from preamble)
851
+ if (def.prompt.preamble) {
852
+ lines.push("## Instructions", "", def.prompt.preamble.trim(), "");
853
+ }
854
+
855
+ // How to use section
856
+ const botMention = config.botUsername ? `@${config.botUsername}` : `@${agentName}`;
857
+ lines.push(
858
+ "## How to Use",
859
+ "",
860
+ `在 GitLab issue 或 MR 中评论 \`${botMention} <你的需求>\`,agent 会自动接单处理。`,
861
+ "",
862
+ "## Status",
863
+ "",
864
+ `> Provider: \`${def.provider}\` | Poll interval: ${def.poll_interval_seconds ?? 30}s`,
865
+ `>`,
866
+ `> *Auto-updated by glab-agent on ${new Date().toISOString().slice(0, 16).replace("T", " ")}*`,
867
+ );
868
+
869
+ const content = lines.join("\n");
870
+
871
+ try {
872
+ const pages = await dependencies.gitlabClient.listWikiPages(projectRef);
873
+ const existing = pages.find(p => p.slug === slug || p.slug === `agents-${agentName}`);
874
+ if (existing) {
875
+ await dependencies.gitlabClient.updateWikiPage(projectRef, existing.slug, slug, content);
876
+ } else {
877
+ await dependencies.gitlabClient.createWikiPage(projectRef, slug, content);
878
+ }
879
+ logger.info(`Wiki profile published: agents/${agentName}`);
880
+ } catch (error) {
881
+ logger.info?.(`Wiki profile update skipped: ${String(error).slice(0, 100)}`);
882
+ }
883
+ }
884
+
800
885
  export async function markAgentOffline(
801
886
  _config: LocalAgentConfig,
802
887
  gitlabClient: GitlabClient,
@@ -1178,6 +1263,7 @@ export async function main(argv: string[] = process.argv.slice(2)): Promise<void
1178
1263
 
1179
1264
  // Update GitLab user bio with agent profile (P1: Agent = 员工)
1180
1265
  await updateAgentUserBio(config, dependencies);
1266
+ await publishAgentProfileWiki(config, dependencies);
1181
1267
  await updateAgentUserStatus(dependencies, "idle", undefined, config);
1182
1268
  } catch (error) {
1183
1269
  logger.error(`Token validation failed for agent "${agentName}": ${String(error)}`);