@clawmem-ai/clawmem 0.1.18 → 0.1.19
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/README.md +28 -9
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/src/collaboration.d.ts +49 -0
- package/dist/src/collaboration.js +69 -0
- package/dist/src/config.d.ts +21 -0
- package/dist/src/config.js +119 -0
- package/dist/src/conversation.d.ts +30 -0
- package/dist/src/conversation.js +323 -0
- package/dist/src/github-client.d.ts +269 -0
- package/dist/src/github-client.js +350 -0
- package/dist/src/keyed-async-queue.d.ts +12 -0
- package/dist/src/keyed-async-queue.js +23 -0
- package/dist/src/memory.d.ts +29 -0
- package/dist/src/memory.js +451 -0
- package/dist/src/recall-sanitize.d.ts +1 -0
- package/dist/src/recall-sanitize.js +149 -0
- package/dist/src/runtime-env.d.ts +2 -0
- package/dist/src/runtime-env.js +12 -0
- package/dist/src/service.d.ts +18 -0
- package/dist/src/service.js +3645 -0
- package/dist/src/state.d.ts +4 -0
- package/dist/src/state.js +182 -0
- package/dist/src/transcript.d.ts +3 -0
- package/dist/src/transcript.js +164 -0
- package/dist/src/types.d.ts +130 -0
- package/dist/src/types.js +1 -0
- package/dist/src/utils.d.ts +26 -0
- package/dist/src/utils.js +62 -0
- package/dist/src/yaml.d.ts +2 -0
- package/dist/src/yaml.js +81 -0
- package/openclaw.plugin.json +14 -1
- package/package.json +21 -7
- package/skills/clawmem/SKILL.md +26 -5
- package/skills/clawmem/references/collaboration.md +13 -5
- package/skills/clawmem/references/review.md +77 -0
- package/skills/clawmem/references/schema.md +44 -1
- package/index.ts +0 -6
- package/src/collaboration.test.ts +0 -71
- package/src/collaboration.ts +0 -109
- package/src/config.test.ts +0 -83
- package/src/config.ts +0 -117
- package/src/conversation.test.ts +0 -120
- package/src/conversation.ts +0 -304
- package/src/github-client.test.ts +0 -101
- package/src/github-client.ts +0 -363
- package/src/keyed-async-queue.ts +0 -26
- package/src/memory.test.ts +0 -588
- package/src/memory.ts +0 -444
- package/src/recall-sanitize.ts +0 -143
- package/src/runtime-env.ts +0 -12
- package/src/service.test.ts +0 -337
- package/src/service.ts +0 -2786
- package/src/state.test.ts +0 -119
- package/src/state.ts +0 -206
- package/src/transcript.ts +0 -186
- package/src/types.ts +0 -86
- package/src/utils.ts +0 -74
- package/src/yaml.ts +0 -88
- package/tsconfig.json +0 -15
package/src/github-client.ts
DELETED
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
// GitHub Issues API client for clawmem. No label caching — idempotent create-if-absent.
|
|
2
|
-
import { resolveLabelColor, labelDescription, extractLabelNames, isManagedLabel } from "./config.js";
|
|
3
|
-
import type { AgentRegistrationResponse, AnonymousSessionResponse, ClawMemResolvedRoute } from "./types.js";
|
|
4
|
-
|
|
5
|
-
type IssueResponse = { number: number; title?: string; body?: string; state?: string; labels?: Array<{ name?: string } | string> };
|
|
6
|
-
type SearchIssuesResponse = { items?: IssueResponse[]; total_count?: number; incomplete_results?: boolean };
|
|
7
|
-
type CommentResponse = { id?: number; body?: string; created_at?: string };
|
|
8
|
-
type LabelResponse = { name?: string; color?: string; description?: string };
|
|
9
|
-
type PermissionMap = Record<string, boolean | undefined>;
|
|
10
|
-
type RepoResponse = {
|
|
11
|
-
name?: string;
|
|
12
|
-
full_name?: string;
|
|
13
|
-
description?: string;
|
|
14
|
-
private?: boolean;
|
|
15
|
-
owner?: { login?: string };
|
|
16
|
-
permissions?: PermissionMap;
|
|
17
|
-
role_name?: string;
|
|
18
|
-
};
|
|
19
|
-
type OrgResponse = {
|
|
20
|
-
id?: number;
|
|
21
|
-
login?: string;
|
|
22
|
-
name?: string;
|
|
23
|
-
description?: string;
|
|
24
|
-
default_repository_permission?: string;
|
|
25
|
-
};
|
|
26
|
-
type TeamResponse = {
|
|
27
|
-
id?: number;
|
|
28
|
-
slug?: string;
|
|
29
|
-
name?: string;
|
|
30
|
-
description?: string;
|
|
31
|
-
privacy?: string;
|
|
32
|
-
permission?: string;
|
|
33
|
-
role_name?: string;
|
|
34
|
-
permissions?: PermissionMap;
|
|
35
|
-
};
|
|
36
|
-
type CollaboratorResponse = {
|
|
37
|
-
id?: number;
|
|
38
|
-
login?: string;
|
|
39
|
-
name?: string;
|
|
40
|
-
permissions?: PermissionMap;
|
|
41
|
-
role_name?: string;
|
|
42
|
-
organization_member?: boolean;
|
|
43
|
-
outside_collaborator?: boolean;
|
|
44
|
-
type?: string;
|
|
45
|
-
};
|
|
46
|
-
type RepositoryInvitationResponse = {
|
|
47
|
-
id?: number;
|
|
48
|
-
created_at?: string;
|
|
49
|
-
permissions?: string;
|
|
50
|
-
repository?: RepoResponse;
|
|
51
|
-
invitee?: { login?: string; name?: string };
|
|
52
|
-
inviter?: { login?: string; name?: string };
|
|
53
|
-
};
|
|
54
|
-
type TeamMembershipResponse = { state?: string; role?: string };
|
|
55
|
-
type OrganizationMembershipResponse = {
|
|
56
|
-
state?: string;
|
|
57
|
-
role?: string;
|
|
58
|
-
organization?: OrgResponse;
|
|
59
|
-
user?: CollaboratorResponse;
|
|
60
|
-
};
|
|
61
|
-
type InvitationResponse = {
|
|
62
|
-
id?: number;
|
|
63
|
-
role?: string;
|
|
64
|
-
created_at?: string;
|
|
65
|
-
expires_at?: string | null;
|
|
66
|
-
email?: string;
|
|
67
|
-
login?: string;
|
|
68
|
-
organization?: OrgResponse;
|
|
69
|
-
invitee?: { login?: string };
|
|
70
|
-
inviter?: { login?: string };
|
|
71
|
-
team_ids?: number[];
|
|
72
|
-
teams?: TeamResponse[];
|
|
73
|
-
};
|
|
74
|
-
type ReqOpts = { allowNotFound?: boolean; allowValidationError?: boolean; omitAuth?: boolean };
|
|
75
|
-
|
|
76
|
-
export class GitHubIssueClient {
|
|
77
|
-
constructor(private readonly config: ClawMemResolvedRoute, private readonly log: { warn?: (msg: string) => void }) {}
|
|
78
|
-
|
|
79
|
-
repo(): string | undefined {
|
|
80
|
-
return this.config.repo?.trim() || undefined;
|
|
81
|
-
}
|
|
82
|
-
defaultRepo(): string | undefined {
|
|
83
|
-
return this.config.defaultRepo?.trim() || undefined;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async createIssue(params: { title: string; body: string; labels: string[] }): Promise<IssueResponse> {
|
|
87
|
-
return this.req<IssueResponse>(this.repoPath("issues"), { method: "POST", body: JSON.stringify(params) });
|
|
88
|
-
}
|
|
89
|
-
async updateIssue(n: number, params: { title?: string; body?: string; state?: "open" | "closed"; labels?: string[] }): Promise<IssueResponse> {
|
|
90
|
-
return this.req<IssueResponse>(this.repoPath(`issues/${n}`), { method: "PATCH", body: JSON.stringify(params) });
|
|
91
|
-
}
|
|
92
|
-
async getIssue(n: number): Promise<IssueResponse> {
|
|
93
|
-
return this.req<IssueResponse>(this.repoPath(`issues/${n}`), { method: "GET" });
|
|
94
|
-
}
|
|
95
|
-
async createComment(issueNumber: number, body: string): Promise<void> {
|
|
96
|
-
await this.req(this.repoPath(`issues/${issueNumber}/comments`), { method: "POST", body: JSON.stringify({ body }) });
|
|
97
|
-
}
|
|
98
|
-
async listComments(issueNumber: number, params?: { page?: number; perPage?: number }): Promise<CommentResponse[]> {
|
|
99
|
-
const q = new URLSearchParams();
|
|
100
|
-
q.set("page", String(params?.page ?? 1));
|
|
101
|
-
q.set("per_page", String(params?.perPage ?? 100));
|
|
102
|
-
return this.req<CommentResponse[]>(`${this.repoPath(`issues/${issueNumber}/comments`)}?${q}`, { method: "GET" });
|
|
103
|
-
}
|
|
104
|
-
async listIssues(params: { labels?: string[]; state?: "open" | "closed" | "all"; page?: number; perPage?: number }): Promise<IssueResponse[]> {
|
|
105
|
-
const q = new URLSearchParams();
|
|
106
|
-
q.set("state", params.state ?? "open"); q.set("page", String(params.page ?? 1)); q.set("per_page", String(params.perPage ?? 100));
|
|
107
|
-
if (params.labels?.length) q.set("labels", params.labels.join(","));
|
|
108
|
-
return this.req<IssueResponse[]>(`${this.repoPath("issues")}?${q}`, { method: "GET" });
|
|
109
|
-
}
|
|
110
|
-
async searchIssues(query: string, params?: { page?: number; perPage?: number }): Promise<IssueResponse[]> {
|
|
111
|
-
const q = new URLSearchParams();
|
|
112
|
-
q.set("q", query);
|
|
113
|
-
q.set("page", String(params?.page ?? 1));
|
|
114
|
-
q.set("per_page", String(params?.perPage ?? 100));
|
|
115
|
-
const res = await this.req<SearchIssuesResponse>(`search/issues?${q}`, { method: "GET" });
|
|
116
|
-
return Array.isArray(res?.items) ? res.items : [];
|
|
117
|
-
}
|
|
118
|
-
async listLabels(params?: { page?: number; perPage?: number }): Promise<LabelResponse[]> {
|
|
119
|
-
const q = new URLSearchParams();
|
|
120
|
-
q.set("page", String(params?.page ?? 1));
|
|
121
|
-
q.set("per_page", String(params?.perPage ?? 100));
|
|
122
|
-
return this.req<LabelResponse[]>(`${this.repoPath("labels")}?${q}`, { method: "GET" });
|
|
123
|
-
}
|
|
124
|
-
async listUserRepos(): Promise<RepoResponse[]> {
|
|
125
|
-
return this.req<RepoResponse[]>("user/repos", { method: "GET" });
|
|
126
|
-
}
|
|
127
|
-
async createUserRepo(params: { name: string; description?: string; private?: boolean; autoInit?: boolean }): Promise<RepoResponse> {
|
|
128
|
-
return this.req<RepoResponse>("user/repos", {
|
|
129
|
-
method: "POST",
|
|
130
|
-
body: JSON.stringify({
|
|
131
|
-
name: params.name,
|
|
132
|
-
...(params.description ? { description: params.description } : {}),
|
|
133
|
-
private: params.private ?? true,
|
|
134
|
-
auto_init: params.autoInit ?? false,
|
|
135
|
-
}),
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
async listUserOrgs(): Promise<OrgResponse[]> {
|
|
139
|
-
return this.req<OrgResponse[]>("user/orgs", { method: "GET" });
|
|
140
|
-
}
|
|
141
|
-
async createUserOrg(params: { login: string; name?: string; defaultRepositoryPermission?: string }): Promise<OrgResponse> {
|
|
142
|
-
return this.req<OrgResponse>("user/orgs", {
|
|
143
|
-
method: "POST",
|
|
144
|
-
body: JSON.stringify({
|
|
145
|
-
login: params.login,
|
|
146
|
-
...(params.name ? { name: params.name } : {}),
|
|
147
|
-
...(params.defaultRepositoryPermission ? { default_repository_permission: params.defaultRepositoryPermission } : {}),
|
|
148
|
-
}),
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
async getOrg(org: string): Promise<OrgResponse> {
|
|
152
|
-
return this.req<OrgResponse>(`orgs/${encodeURIComponent(org)}`, { method: "GET" });
|
|
153
|
-
}
|
|
154
|
-
async listOrgMembers(org: string, role?: "admin"): Promise<CollaboratorResponse[]> {
|
|
155
|
-
const q = new URLSearchParams();
|
|
156
|
-
if (role) q.set("role", role);
|
|
157
|
-
const suffix = q.toString();
|
|
158
|
-
return this.req<CollaboratorResponse[]>(`orgs/${encodeURIComponent(org)}/members${suffix ? `?${suffix}` : ""}`, { method: "GET" });
|
|
159
|
-
}
|
|
160
|
-
async getOrgMembership(org: string, username: string): Promise<OrganizationMembershipResponse> {
|
|
161
|
-
return this.req<OrganizationMembershipResponse>(
|
|
162
|
-
`orgs/${encodeURIComponent(org)}/memberships/${encodeURIComponent(username)}`,
|
|
163
|
-
{ method: "GET" },
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
async removeOrgMember(org: string, username: string): Promise<void> {
|
|
167
|
-
await this.req(`orgs/${encodeURIComponent(org)}/members/${encodeURIComponent(username)}`, { method: "DELETE" });
|
|
168
|
-
}
|
|
169
|
-
async removeOrgMembership(org: string, username: string): Promise<void> {
|
|
170
|
-
await this.req(`orgs/${encodeURIComponent(org)}/memberships/${encodeURIComponent(username)}`, { method: "DELETE" });
|
|
171
|
-
}
|
|
172
|
-
async listOrgTeams(org: string): Promise<TeamResponse[]> {
|
|
173
|
-
return this.req<TeamResponse[]>(`orgs/${encodeURIComponent(org)}/teams`, { method: "GET" });
|
|
174
|
-
}
|
|
175
|
-
async getTeam(org: string, teamSlug: string): Promise<TeamResponse> {
|
|
176
|
-
return this.req<TeamResponse>(`orgs/${encodeURIComponent(org)}/teams/${encodeURIComponent(teamSlug)}`, { method: "GET" });
|
|
177
|
-
}
|
|
178
|
-
async createOrgTeam(org: string, params: { name: string; description?: string; privacy?: "closed" | "secret" }): Promise<TeamResponse> {
|
|
179
|
-
return this.req<TeamResponse>(`orgs/${encodeURIComponent(org)}/teams`, {
|
|
180
|
-
method: "POST",
|
|
181
|
-
body: JSON.stringify({
|
|
182
|
-
name: params.name,
|
|
183
|
-
...(params.description ? { description: params.description } : {}),
|
|
184
|
-
privacy: params.privacy ?? "closed",
|
|
185
|
-
}),
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
async updateTeam(
|
|
189
|
-
org: string,
|
|
190
|
-
teamSlug: string,
|
|
191
|
-
params: { name?: string; description?: string; privacy?: "closed" | "secret" },
|
|
192
|
-
): Promise<TeamResponse> {
|
|
193
|
-
return this.req<TeamResponse>(`orgs/${encodeURIComponent(org)}/teams/${encodeURIComponent(teamSlug)}`, {
|
|
194
|
-
method: "PATCH",
|
|
195
|
-
body: JSON.stringify({
|
|
196
|
-
...(params.name ? { name: params.name } : {}),
|
|
197
|
-
...(params.description ? { description: params.description } : {}),
|
|
198
|
-
...(params.privacy ? { privacy: params.privacy } : {}),
|
|
199
|
-
}),
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
async deleteTeam(org: string, teamSlug: string): Promise<void> {
|
|
203
|
-
await this.req(`orgs/${encodeURIComponent(org)}/teams/${encodeURIComponent(teamSlug)}`, { method: "DELETE" });
|
|
204
|
-
}
|
|
205
|
-
async listTeamMembers(org: string, teamSlug: string): Promise<CollaboratorResponse[]> {
|
|
206
|
-
return this.req<CollaboratorResponse[]>(
|
|
207
|
-
`orgs/${encodeURIComponent(org)}/teams/${encodeURIComponent(teamSlug)}/members`,
|
|
208
|
-
{ method: "GET" },
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
async setTeamMembership(org: string, teamSlug: string, username: string, role: "member" | "maintainer"): Promise<TeamMembershipResponse> {
|
|
212
|
-
return this.req<TeamMembershipResponse>(
|
|
213
|
-
`orgs/${encodeURIComponent(org)}/teams/${encodeURIComponent(teamSlug)}/memberships/${encodeURIComponent(username)}`,
|
|
214
|
-
{ method: "PUT", body: JSON.stringify({ role }) },
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
async removeTeamMembership(org: string, teamSlug: string, username: string): Promise<void> {
|
|
218
|
-
await this.req(
|
|
219
|
-
`orgs/${encodeURIComponent(org)}/teams/${encodeURIComponent(teamSlug)}/memberships/${encodeURIComponent(username)}`,
|
|
220
|
-
{ method: "DELETE" },
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
async listTeamRepos(org: string, teamSlug: string): Promise<RepoResponse[]> {
|
|
224
|
-
return this.req<RepoResponse[]>(`orgs/${encodeURIComponent(org)}/teams/${encodeURIComponent(teamSlug)}/repos`, { method: "GET" });
|
|
225
|
-
}
|
|
226
|
-
async setTeamRepoAccess(org: string, teamSlug: string, owner: string, repo: string, permission: "read" | "write" | "admin"): Promise<void> {
|
|
227
|
-
await this.req(
|
|
228
|
-
`orgs/${encodeURIComponent(org)}/teams/${encodeURIComponent(teamSlug)}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`,
|
|
229
|
-
{ method: "PUT", body: JSON.stringify({ permission }) },
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
async removeTeamRepoAccess(org: string, teamSlug: string, owner: string, repo: string): Promise<void> {
|
|
233
|
-
await this.req(
|
|
234
|
-
`orgs/${encodeURIComponent(org)}/teams/${encodeURIComponent(teamSlug)}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`,
|
|
235
|
-
{ method: "DELETE" },
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
async listRepoCollaborators(owner: string, repo: string): Promise<CollaboratorResponse[]> {
|
|
239
|
-
return this.req<CollaboratorResponse[]>(
|
|
240
|
-
`repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/collaborators`,
|
|
241
|
-
{ method: "GET" },
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
async listRepoInvitations(owner: string, repo: string): Promise<RepositoryInvitationResponse[]> {
|
|
245
|
-
return this.req<RepositoryInvitationResponse[]>(
|
|
246
|
-
`repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/invitations`,
|
|
247
|
-
{ method: "GET" },
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
async setRepoCollaborator(owner: string, repo: string, username: string, permission: "read" | "write" | "admin"): Promise<RepositoryInvitationResponse | undefined> {
|
|
251
|
-
return this.req<RepositoryInvitationResponse>(
|
|
252
|
-
`repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/collaborators/${encodeURIComponent(username)}`,
|
|
253
|
-
{ method: "PUT", body: JSON.stringify({ permission }) },
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
async removeRepoCollaborator(owner: string, repo: string, username: string): Promise<void> {
|
|
257
|
-
await this.req(
|
|
258
|
-
`repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/collaborators/${encodeURIComponent(username)}`,
|
|
259
|
-
{ method: "DELETE" },
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
async getRepo(owner: string, repo: string): Promise<RepoResponse> {
|
|
263
|
-
return this.req<RepoResponse>(`repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`, { method: "GET" });
|
|
264
|
-
}
|
|
265
|
-
async listUserRepoInvitations(): Promise<RepositoryInvitationResponse[]> {
|
|
266
|
-
return this.req<RepositoryInvitationResponse[]>("user/repository_invitations", { method: "GET" });
|
|
267
|
-
}
|
|
268
|
-
async acceptUserRepoInvitation(invitationId: number): Promise<void> {
|
|
269
|
-
await this.req(`user/repository_invitations/${invitationId}`, { method: "PATCH" });
|
|
270
|
-
}
|
|
271
|
-
async declineUserRepoInvitation(invitationId: number): Promise<void> {
|
|
272
|
-
await this.req(`user/repository_invitations/${invitationId}`, { method: "DELETE" });
|
|
273
|
-
}
|
|
274
|
-
async listOrgInvitations(org: string): Promise<InvitationResponse[]> {
|
|
275
|
-
return this.req<InvitationResponse[]>(`orgs/${encodeURIComponent(org)}/invitations`, { method: "GET" });
|
|
276
|
-
}
|
|
277
|
-
async createOrgInvitation(
|
|
278
|
-
org: string,
|
|
279
|
-
params: { inviteeLogin: string; role?: "member" | "owner"; teamIds?: number[]; expiresInDays?: number },
|
|
280
|
-
): Promise<InvitationResponse> {
|
|
281
|
-
return this.req<InvitationResponse>(`orgs/${encodeURIComponent(org)}/invitations`, {
|
|
282
|
-
method: "POST",
|
|
283
|
-
body: JSON.stringify({
|
|
284
|
-
invitee_login: params.inviteeLogin,
|
|
285
|
-
role: params.role ?? "member",
|
|
286
|
-
...(params.teamIds && params.teamIds.length > 0 ? { team_ids: params.teamIds } : {}),
|
|
287
|
-
...(typeof params.expiresInDays === "number" ? { expires_in_days: params.expiresInDays } : {}),
|
|
288
|
-
}),
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
async revokeOrgInvitation(org: string, invitationId: number): Promise<void> {
|
|
292
|
-
await this.req(`orgs/${encodeURIComponent(org)}/invitations/${invitationId}`, { method: "DELETE" });
|
|
293
|
-
}
|
|
294
|
-
async listOrgOutsideCollaborators(org: string): Promise<CollaboratorResponse[]> {
|
|
295
|
-
return this.req<CollaboratorResponse[]>(`orgs/${encodeURIComponent(org)}/outside_collaborators`, { method: "GET" });
|
|
296
|
-
}
|
|
297
|
-
async listUserOrgInvitations(): Promise<InvitationResponse[]> {
|
|
298
|
-
return this.req<InvitationResponse[]>("user/organization_invitations", { method: "GET" });
|
|
299
|
-
}
|
|
300
|
-
async acceptUserOrgInvitation(invitationId: number): Promise<void> {
|
|
301
|
-
await this.req(`user/organization_invitations/${invitationId}`, { method: "PATCH" });
|
|
302
|
-
}
|
|
303
|
-
async declineUserOrgInvitation(invitationId: number): Promise<void> {
|
|
304
|
-
await this.req(`user/organization_invitations/${invitationId}`, { method: "DELETE" });
|
|
305
|
-
}
|
|
306
|
-
async transferRepo(owner: string, repo: string, newOwner: string): Promise<RepoResponse> {
|
|
307
|
-
return this.req<RepoResponse>(`repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/transfer`, {
|
|
308
|
-
method: "POST",
|
|
309
|
-
body: JSON.stringify({ new_owner: newOwner }),
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
async ensureLabels(labels: string[]): Promise<void> {
|
|
313
|
-
for (const label of labels) {
|
|
314
|
-
if (!label.trim()) continue;
|
|
315
|
-
await this.req(this.repoPath("labels"), { method: "POST",
|
|
316
|
-
body: JSON.stringify({ name: label, color: resolveLabelColor(label), description: labelDescription(label) }) }, { allowValidationError: true });
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
async syncManagedLabels(issueNumber: number, desired: string[]): Promise<void> {
|
|
320
|
-
const issue = await this.getIssue(issueNumber);
|
|
321
|
-
const unmanaged = extractLabelNames(issue.labels).filter((l) => !isManagedLabel(l));
|
|
322
|
-
await this.updateIssue(issueNumber, { labels: [...new Set([...unmanaged, ...desired])] });
|
|
323
|
-
}
|
|
324
|
-
async getRepoInfo(): Promise<{ description?: string; name?: string }> {
|
|
325
|
-
return this.req<{ description?: string; name?: string }>(this.repoPath("").replace(/\/$/, ""), { method: "GET" });
|
|
326
|
-
}
|
|
327
|
-
async updateRepoDescription(description: string): Promise<void> {
|
|
328
|
-
await this.req(this.repoPath("").replace(/\/$/, ""), { method: "PATCH", body: JSON.stringify({ description }) });
|
|
329
|
-
}
|
|
330
|
-
async registerAgent(prefixLogin: string, defaultRepoName: string): Promise<AgentRegistrationResponse> {
|
|
331
|
-
return this.req<AgentRegistrationResponse>("agents", {
|
|
332
|
-
method: "POST",
|
|
333
|
-
body: JSON.stringify({
|
|
334
|
-
prefix_login: prefixLogin,
|
|
335
|
-
default_repo_name: defaultRepoName,
|
|
336
|
-
}),
|
|
337
|
-
}, { omitAuth: true });
|
|
338
|
-
}
|
|
339
|
-
async createAnonymousSession(locale?: string): Promise<AnonymousSessionResponse> {
|
|
340
|
-
const body = locale ? JSON.stringify({ locale }) : undefined;
|
|
341
|
-
return this.req<AnonymousSessionResponse>("anonymous/session", { method: "POST", ...(body ? { body } : {}) }, { omitAuth: true });
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
private repoPath(suffix: string): string {
|
|
345
|
-
if (!this.config.repo) throw new Error("clawmem repository is not configured");
|
|
346
|
-
return `repos/${this.config.repo}/${suffix}`;
|
|
347
|
-
}
|
|
348
|
-
private async req<T = void>(pathname: string, init: RequestInit, opts: ReqOpts = {}): Promise<T> {
|
|
349
|
-
if (!this.config.baseUrl) throw new Error("clawmem baseUrl is not configured");
|
|
350
|
-
if (!opts.omitAuth && !this.config.token) throw new Error("clawmem token is not configured");
|
|
351
|
-
const base = this.config.baseUrl.replace(/\/+$/, "");
|
|
352
|
-
const headers: Record<string, string> = { Accept: "application/vnd.github+json", "Content-Type": "application/json" };
|
|
353
|
-
if (!opts.omitAuth) headers.Authorization = this.config.authScheme === "bearer" ? `Bearer ${this.config.token}` : `token ${this.config.token}`;
|
|
354
|
-
const res = await fetch(new URL(pathname, `${base}/`), { ...init, headers: { ...headers, ...(init.headers ?? {}) } });
|
|
355
|
-
if (res.status === 404 && opts.allowNotFound) return undefined as T;
|
|
356
|
-
if (res.status === 422 && opts.allowValidationError) return undefined as T;
|
|
357
|
-
if (!res.ok) { const d = await res.text(); throw new Error(`HTTP ${res.status}: ${d || res.statusText}`); }
|
|
358
|
-
if (res.status === 204) return undefined as T;
|
|
359
|
-
const text = await res.text();
|
|
360
|
-
if (!text.trim()) return undefined as T;
|
|
361
|
-
try { return JSON.parse(text) as T; } catch (e) { this.log.warn?.(`clawmem: failed to parse API response: ${String(e)}`); return undefined as T; }
|
|
362
|
-
}
|
|
363
|
-
}
|
package/src/keyed-async-queue.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A keyed async queue that serializes async tasks per key.
|
|
3
|
-
* Inlined from openclaw/plugin-sdk/keyed-async-queue to avoid
|
|
4
|
-
* dependency on platform internals that may not exist in older versions.
|
|
5
|
-
*
|
|
6
|
-
* Matches the behaviour of the upstream implementation: a failed task
|
|
7
|
-
* does not block subsequent tasks on the same key.
|
|
8
|
-
*/
|
|
9
|
-
export class KeyedAsyncQueue {
|
|
10
|
-
private readonly tails = new Map<string, Promise<void>>();
|
|
11
|
-
|
|
12
|
-
enqueue<T>(key: string, task: () => Promise<T>): Promise<T> {
|
|
13
|
-
const current = (this.tails.get(key) ?? Promise.resolve())
|
|
14
|
-
.catch(() => void 0)
|
|
15
|
-
.then(task);
|
|
16
|
-
const tail = current.then(
|
|
17
|
-
() => void 0,
|
|
18
|
-
() => void 0,
|
|
19
|
-
);
|
|
20
|
-
this.tails.set(key, tail);
|
|
21
|
-
tail.finally(() => {
|
|
22
|
-
if (this.tails.get(key) === tail) this.tails.delete(key);
|
|
23
|
-
});
|
|
24
|
-
return current;
|
|
25
|
-
}
|
|
26
|
-
}
|