@aigne/afs-persona 1.11.0-beta.10
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/LICENSE.md +26 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
- package/dist/db.cjs +206 -0
- package/dist/db.d.cts +93 -0
- package/dist/db.d.cts.map +1 -0
- package/dist/db.d.mts +93 -0
- package/dist/db.d.mts.map +1 -0
- package/dist/db.mjs +207 -0
- package/dist/db.mjs.map +1 -0
- package/dist/index.cjs +987 -0
- package/dist/index.d.cts +121 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +121 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +987 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,987 @@
|
|
|
1
|
+
import { PersonaDB } from "./db.mjs";
|
|
2
|
+
import { __decorate } from "./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { readdir } from "node:fs/promises";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { AFSBaseProvider, Actions, Explain, List, Meta, Read, Stat } from "@aigne/afs";
|
|
7
|
+
import { zodParse } from "@aigne/afs/utils/zod";
|
|
8
|
+
import { joinURL } from "ufo";
|
|
9
|
+
import { parse, stringify } from "yaml";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
//#region src/index.ts
|
|
13
|
+
const personaOptionsSchema = z.object({
|
|
14
|
+
name: z.string().optional(),
|
|
15
|
+
description: z.string().optional(),
|
|
16
|
+
accessMode: z.enum(["readonly", "readwrite"]).optional(),
|
|
17
|
+
dataDir: z.string().describe("Root data directory for persona storage"),
|
|
18
|
+
database: z.string().optional().describe("SQLite database path (defaults to dataDir/persona.db)"),
|
|
19
|
+
model: z.string().optional().describe("Default LLM model path for ASH agent-run"),
|
|
20
|
+
ashPath: z.string().optional().describe("ASH provider mount path"),
|
|
21
|
+
tickInterval: z.number().positive().optional().describe("Tick interval in milliseconds"),
|
|
22
|
+
safety: z.object({
|
|
23
|
+
blocked_keywords: z.array(z.string()).optional(),
|
|
24
|
+
review_mode: z.enum(["auto", "manual"]).optional()
|
|
25
|
+
}).optional()
|
|
26
|
+
});
|
|
27
|
+
const INVALID_NAME_PATTERN = /[/\\]/;
|
|
28
|
+
const MAX_NAME_LENGTH = 64;
|
|
29
|
+
function validateCharacterName(name) {
|
|
30
|
+
if (typeof name !== "string" || name.length === 0) return {
|
|
31
|
+
valid: false,
|
|
32
|
+
reason: "Character name must be a non-empty string"
|
|
33
|
+
};
|
|
34
|
+
if (name === "." || name === "..") return {
|
|
35
|
+
valid: false,
|
|
36
|
+
reason: "Character name cannot be . or .."
|
|
37
|
+
};
|
|
38
|
+
if (name.includes("..")) return {
|
|
39
|
+
valid: false,
|
|
40
|
+
reason: "Character name cannot contain path traversal"
|
|
41
|
+
};
|
|
42
|
+
if (INVALID_NAME_PATTERN.test(name)) return {
|
|
43
|
+
valid: false,
|
|
44
|
+
reason: "Character name cannot contain / or \\"
|
|
45
|
+
};
|
|
46
|
+
if (name.length > MAX_NAME_LENGTH) return {
|
|
47
|
+
valid: false,
|
|
48
|
+
reason: `Character name cannot exceed ${MAX_NAME_LENGTH} characters`
|
|
49
|
+
};
|
|
50
|
+
return { valid: true };
|
|
51
|
+
}
|
|
52
|
+
const WORKSPACE_TOML_TEMPLATE = `[[mounts]]
|
|
53
|
+
path = "/profile"
|
|
54
|
+
uri = "json://./profile.yaml"
|
|
55
|
+
access_mode = "readwrite"
|
|
56
|
+
|
|
57
|
+
[[mounts]]
|
|
58
|
+
path = "/browser"
|
|
59
|
+
uri = "mcp+stdio://npx @anthropic-ai/mcp-server-playwright"
|
|
60
|
+
[mounts.options]
|
|
61
|
+
userDataDir = "./browser"
|
|
62
|
+
|
|
63
|
+
[[mounts]]
|
|
64
|
+
path = "/data"
|
|
65
|
+
uri = "fs://./data"
|
|
66
|
+
access_mode = "readwrite"
|
|
67
|
+
`;
|
|
68
|
+
var AFSPersonaProvider = class AFSPersonaProvider extends AFSBaseProvider {
|
|
69
|
+
name;
|
|
70
|
+
description;
|
|
71
|
+
accessMode;
|
|
72
|
+
dataDir;
|
|
73
|
+
database;
|
|
74
|
+
model;
|
|
75
|
+
ashPath;
|
|
76
|
+
tickInterval;
|
|
77
|
+
safety;
|
|
78
|
+
afs;
|
|
79
|
+
db;
|
|
80
|
+
tickTimer;
|
|
81
|
+
destroyed = false;
|
|
82
|
+
constructor(options) {
|
|
83
|
+
super();
|
|
84
|
+
const validated = personaOptionsSchema.parse(options);
|
|
85
|
+
this.name = validated.name || "persona";
|
|
86
|
+
this.description = validated.description || "Virtual persona management";
|
|
87
|
+
this.accessMode = validated.accessMode || "readwrite";
|
|
88
|
+
this.dataDir = resolve(validated.dataDir);
|
|
89
|
+
this.database = validated.database || resolve(this.dataDir, "persona.db");
|
|
90
|
+
this.model = validated.model || "/modules/aignehub/defaults/chat";
|
|
91
|
+
this.ashPath = validated.ashPath || "/modules/ash";
|
|
92
|
+
this.tickInterval = validated.tickInterval || 300 * 1e3;
|
|
93
|
+
this.safety = validated.safety || {};
|
|
94
|
+
if (!existsSync(this.dataDir)) mkdirSync(this.dataDir, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
static schema() {
|
|
97
|
+
return personaOptionsSchema;
|
|
98
|
+
}
|
|
99
|
+
static checkKeywords(content, keywords) {
|
|
100
|
+
if (!content || keywords.length === 0) return { blocked: false };
|
|
101
|
+
const lowerContent = content.toLowerCase();
|
|
102
|
+
for (const keyword of keywords) if (lowerContent.includes(keyword.toLowerCase())) return {
|
|
103
|
+
blocked: true,
|
|
104
|
+
keyword
|
|
105
|
+
};
|
|
106
|
+
return { blocked: false };
|
|
107
|
+
}
|
|
108
|
+
loadBlockedKeywords() {
|
|
109
|
+
const entries = this.safety.blocked_keywords || [];
|
|
110
|
+
const keywords = [];
|
|
111
|
+
for (const entry of entries) {
|
|
112
|
+
const resolved = resolve(this.dataDir, entry);
|
|
113
|
+
if (!resolved.startsWith(`${this.dataDir}/`) && resolved !== this.dataDir) continue;
|
|
114
|
+
if (!existsSync(resolved)) continue;
|
|
115
|
+
const content = readFileSync(resolved, "utf-8");
|
|
116
|
+
for (const line of content.split("\n")) {
|
|
117
|
+
const trimmed = line.trim();
|
|
118
|
+
if (trimmed) keywords.push(trimmed);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return keywords;
|
|
122
|
+
}
|
|
123
|
+
static manifest() {
|
|
124
|
+
return {
|
|
125
|
+
name: "persona",
|
|
126
|
+
description: "Virtual persona manager — creates isolated character workspaces, drives session-based social media interaction via ASH agent-run.",
|
|
127
|
+
uriTemplate: "persona://{dataDir}",
|
|
128
|
+
category: "agent",
|
|
129
|
+
schema: z.object({
|
|
130
|
+
dataDir: z.string(),
|
|
131
|
+
database: z.string().optional(),
|
|
132
|
+
model: z.string().optional(),
|
|
133
|
+
ashPath: z.string().optional(),
|
|
134
|
+
tickInterval: z.number().optional()
|
|
135
|
+
}),
|
|
136
|
+
tags: [
|
|
137
|
+
"persona",
|
|
138
|
+
"social",
|
|
139
|
+
"agent",
|
|
140
|
+
"automation"
|
|
141
|
+
],
|
|
142
|
+
capabilityTags: ["read-write"],
|
|
143
|
+
security: {
|
|
144
|
+
riskLevel: "external",
|
|
145
|
+
resourceAccess: ["internet", "process-spawn"],
|
|
146
|
+
requires: ["sqlite"],
|
|
147
|
+
dataSensitivity: ["personal-data"],
|
|
148
|
+
notes: ["Automates social media interactions via Playwright browser"]
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
static treeSchema() {
|
|
153
|
+
return {
|
|
154
|
+
operations: [
|
|
155
|
+
"list",
|
|
156
|
+
"read",
|
|
157
|
+
"exec",
|
|
158
|
+
"stat",
|
|
159
|
+
"explain"
|
|
160
|
+
],
|
|
161
|
+
tree: {
|
|
162
|
+
"/": {
|
|
163
|
+
kind: "persona:root",
|
|
164
|
+
operations: ["list", "read"],
|
|
165
|
+
actions: [
|
|
166
|
+
"create-character",
|
|
167
|
+
"delete-character",
|
|
168
|
+
"tick",
|
|
169
|
+
"dispatch-task"
|
|
170
|
+
]
|
|
171
|
+
},
|
|
172
|
+
"/characters": {
|
|
173
|
+
kind: "persona:collection",
|
|
174
|
+
operations: ["list"]
|
|
175
|
+
},
|
|
176
|
+
"/characters/{name}": {
|
|
177
|
+
kind: "persona:character",
|
|
178
|
+
operations: [
|
|
179
|
+
"list",
|
|
180
|
+
"read",
|
|
181
|
+
"exec"
|
|
182
|
+
],
|
|
183
|
+
actions: [
|
|
184
|
+
"session",
|
|
185
|
+
"post",
|
|
186
|
+
"reply",
|
|
187
|
+
"pause",
|
|
188
|
+
"resume"
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
bestFor: [
|
|
193
|
+
"persona management",
|
|
194
|
+
"social media automation",
|
|
195
|
+
"character orchestration"
|
|
196
|
+
],
|
|
197
|
+
notFor: ["file storage", "databases"]
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
static async load({ basePath, config } = {}) {
|
|
201
|
+
return new AFSPersonaProvider(zodParse(personaOptionsSchema, config, { prefix: basePath }));
|
|
202
|
+
}
|
|
203
|
+
onMount(afs, _mountPath) {
|
|
204
|
+
this.afs = afs;
|
|
205
|
+
this.scheduleNextTick();
|
|
206
|
+
}
|
|
207
|
+
get charactersDir() {
|
|
208
|
+
return resolve(this.dataDir, "characters");
|
|
209
|
+
}
|
|
210
|
+
async getCharacterNames() {
|
|
211
|
+
if (!existsSync(this.charactersDir)) return [];
|
|
212
|
+
return (await readdir(this.charactersDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
213
|
+
}
|
|
214
|
+
characterDir(name) {
|
|
215
|
+
return resolve(this.charactersDir, name);
|
|
216
|
+
}
|
|
217
|
+
characterProfilePath(name) {
|
|
218
|
+
return resolve(this.characterDir(name), "profile.yaml");
|
|
219
|
+
}
|
|
220
|
+
readCharacterProfile(name) {
|
|
221
|
+
const profilePath = this.characterProfilePath(name);
|
|
222
|
+
if (!existsSync(profilePath)) return null;
|
|
223
|
+
return parse(readFileSync(profilePath, "utf-8"));
|
|
224
|
+
}
|
|
225
|
+
writeCharacterProfile(name, profile) {
|
|
226
|
+
writeFileSync(this.characterProfilePath(name), stringify(profile), "utf-8");
|
|
227
|
+
}
|
|
228
|
+
getDB() {
|
|
229
|
+
if (!this.db) this.db = new PersonaDB(this.database);
|
|
230
|
+
return this.db;
|
|
231
|
+
}
|
|
232
|
+
scheduleNextTick() {
|
|
233
|
+
if (this.destroyed) return;
|
|
234
|
+
this.tickTimer = setTimeout(async () => {
|
|
235
|
+
try {
|
|
236
|
+
await this.runTick();
|
|
237
|
+
} catch {}
|
|
238
|
+
this.scheduleNextTick();
|
|
239
|
+
}, this.tickInterval);
|
|
240
|
+
}
|
|
241
|
+
shouldStartSession(gap, offlineMinutes) {
|
|
242
|
+
const gapMin = gap[0] ?? 30;
|
|
243
|
+
const gapMax = gap[1] ?? 240;
|
|
244
|
+
if (offlineMinutes >= gapMax) return true;
|
|
245
|
+
if (offlineMinutes < gapMin) return false;
|
|
246
|
+
if (gapMax <= gapMin) return true;
|
|
247
|
+
const probability = (offlineMinutes - gapMin) / (gapMax - gapMin);
|
|
248
|
+
return Math.random() < probability;
|
|
249
|
+
}
|
|
250
|
+
async runTick(characterFilter) {
|
|
251
|
+
const db = this.getDB();
|
|
252
|
+
const stale_recovered = db.markStaleSessions(60);
|
|
253
|
+
const characterNames = characterFilter ? [characterFilter] : await this.getCharacterNames();
|
|
254
|
+
let sessions_started = 0;
|
|
255
|
+
for (const name of characterNames) {
|
|
256
|
+
const profile = this.readCharacterProfile(name);
|
|
257
|
+
if (!profile) continue;
|
|
258
|
+
if (profile.status === "paused") continue;
|
|
259
|
+
if (db.getRunningSession(name)) continue;
|
|
260
|
+
const lastDone = db.getLastDoneSession(name);
|
|
261
|
+
let offlineMinutes;
|
|
262
|
+
if (!lastDone || !lastDone.ended_at) offlineMinutes = Number.POSITIVE_INFINITY;
|
|
263
|
+
else {
|
|
264
|
+
const endedAt = new Date(lastDone.ended_at).getTime();
|
|
265
|
+
offlineMinutes = (Date.now() - endedAt) / (60 * 1e3);
|
|
266
|
+
}
|
|
267
|
+
const gap = Array.isArray(profile.gap) ? profile.gap : [30, 240];
|
|
268
|
+
if (!this.shouldStartSession(gap, offlineMinutes)) continue;
|
|
269
|
+
const sessionId = db.insertSession(name);
|
|
270
|
+
db.updateSession(sessionId, {
|
|
271
|
+
status: "done",
|
|
272
|
+
ended_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
273
|
+
});
|
|
274
|
+
sessions_started++;
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
sessions_started,
|
|
278
|
+
stale_recovered
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
async runSessionSteps(name, profile, _sessionId) {
|
|
282
|
+
const db = this.getDB();
|
|
283
|
+
const platforms = profile.platforms || [];
|
|
284
|
+
const model = profile.model || this.model;
|
|
285
|
+
const recentInteractions = db.getRecentInteractions(name, 10);
|
|
286
|
+
let steps_completed = 0;
|
|
287
|
+
let interactions_saved = 0;
|
|
288
|
+
for (const platform of platforms) {
|
|
289
|
+
const steps = this.buildStepsForPlatform(name, platform);
|
|
290
|
+
for (const step of steps) {
|
|
291
|
+
const result = await this.executeStep(name, profile, platform, step, model, recentInteractions);
|
|
292
|
+
if (result.trace) try {
|
|
293
|
+
db.insertInteraction({
|
|
294
|
+
character: name,
|
|
295
|
+
platform,
|
|
296
|
+
type: step.name,
|
|
297
|
+
content: null,
|
|
298
|
+
metadata: {
|
|
299
|
+
step: step.name,
|
|
300
|
+
trace_rounds: Array.isArray(result.trace) ? result.trace.length : 0,
|
|
301
|
+
status: result.status
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
interactions_saved++;
|
|
305
|
+
} catch {}
|
|
306
|
+
if (result.status === "error") throw new Error(`Step '${step.name}' failed`);
|
|
307
|
+
if (result.status !== "budget_exhausted") steps_completed++;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
steps_completed,
|
|
312
|
+
interactions_saved
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
buildStepsForPlatform(name, platform) {
|
|
316
|
+
const db = this.getDB();
|
|
317
|
+
const steps = [{
|
|
318
|
+
name: "notifications",
|
|
319
|
+
platform,
|
|
320
|
+
budget: { max_rounds: 15 }
|
|
321
|
+
}, {
|
|
322
|
+
name: "browse",
|
|
323
|
+
platform,
|
|
324
|
+
budget: { max_rounds: 25 }
|
|
325
|
+
}];
|
|
326
|
+
if (Math.random() < .3) steps.push({
|
|
327
|
+
name: "post",
|
|
328
|
+
platform,
|
|
329
|
+
budget: { max_rounds: 20 }
|
|
330
|
+
});
|
|
331
|
+
const seededTopics = db.getSeededTopics(name, "pending");
|
|
332
|
+
for (const topic of seededTopics) if (db.claimSeededTopic(topic.id, topic.task_id)) steps.push({
|
|
333
|
+
name: "seeded_topic",
|
|
334
|
+
platform,
|
|
335
|
+
topic: topic.topic,
|
|
336
|
+
angle: topic.angle,
|
|
337
|
+
budget: { max_rounds: 20 }
|
|
338
|
+
});
|
|
339
|
+
return steps;
|
|
340
|
+
}
|
|
341
|
+
async executeStep(name, profile, platform, step, model, recentInteractions) {
|
|
342
|
+
const system = this.buildSystemPrompt(name, profile, platform, recentInteractions);
|
|
343
|
+
const tools = this.buildToolsWhitelist(name);
|
|
344
|
+
const task = this.buildStepTask(step, name, platform);
|
|
345
|
+
try {
|
|
346
|
+
const result = await this.afs.exec(joinURL(this.ashPath, ".actions/agent-run"), {
|
|
347
|
+
task,
|
|
348
|
+
model,
|
|
349
|
+
system,
|
|
350
|
+
tools,
|
|
351
|
+
budget: step.budget
|
|
352
|
+
}, {});
|
|
353
|
+
if (!result.success) return {
|
|
354
|
+
status: "error",
|
|
355
|
+
error: result.error?.message
|
|
356
|
+
};
|
|
357
|
+
const data = result.data || {};
|
|
358
|
+
const trace = data.trace;
|
|
359
|
+
if (data.status === "error") return {
|
|
360
|
+
status: "error",
|
|
361
|
+
error: String(data.message || "Unknown error"),
|
|
362
|
+
trace
|
|
363
|
+
};
|
|
364
|
+
if (data.status === "budget_exhausted") return {
|
|
365
|
+
status: "budget_exhausted",
|
|
366
|
+
trace
|
|
367
|
+
};
|
|
368
|
+
return {
|
|
369
|
+
status: "completed",
|
|
370
|
+
trace
|
|
371
|
+
};
|
|
372
|
+
} catch (e) {
|
|
373
|
+
return {
|
|
374
|
+
status: "error",
|
|
375
|
+
error: e instanceof Error ? e.message : "Unknown error"
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
buildSystemPrompt(name, profile, platform, recentInteractions) {
|
|
380
|
+
const personality = profile.personality || {};
|
|
381
|
+
const interests = profile.interests || [];
|
|
382
|
+
const parts = [];
|
|
383
|
+
parts.push(`你是 ${name}。${personality.tone || "neutral"}。`);
|
|
384
|
+
parts.push(`兴趣:${interests.map((i) => i.topic).join(", ") || "广泛"}。风格:${personality.style || "简洁"}。语言:${personality.language || "en"}。`);
|
|
385
|
+
if (recentInteractions.length > 0) {
|
|
386
|
+
parts.push("最近发过的内容:");
|
|
387
|
+
for (const interaction of recentInteractions.slice(0, 5)) parts.push(`- [${interaction.type}] ${interaction.content || "(no content)"}`);
|
|
388
|
+
}
|
|
389
|
+
parts.push(`当前平台:${platform}`);
|
|
390
|
+
return parts.join("\n");
|
|
391
|
+
}
|
|
392
|
+
buildToolsWhitelist(characterName) {
|
|
393
|
+
return [
|
|
394
|
+
{
|
|
395
|
+
path: `/modules/persona/characters/${characterName}/browser/tools/*`,
|
|
396
|
+
ops: ["exec"]
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
path: `/modules/persona/characters/${characterName}/browser/tools`,
|
|
400
|
+
ops: ["list"]
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
path: `/modules/persona/characters/${characterName}/profile/**`,
|
|
404
|
+
ops: ["read", "list"],
|
|
405
|
+
maxDepth: 3
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
path: `/modules/persona/characters/${characterName}/data/**`,
|
|
409
|
+
ops: [
|
|
410
|
+
"read",
|
|
411
|
+
"write",
|
|
412
|
+
"list"
|
|
413
|
+
],
|
|
414
|
+
maxDepth: 3
|
|
415
|
+
}
|
|
416
|
+
];
|
|
417
|
+
}
|
|
418
|
+
buildStepTask(step, name, platform) {
|
|
419
|
+
switch (step.name) {
|
|
420
|
+
case "notifications": return `Check notifications on ${platform} and reply to any mentions or messages as ${name}.`;
|
|
421
|
+
case "browse": return `Browse the ${platform} feed, engage with interesting posts that match your interests.`;
|
|
422
|
+
case "post": return `Create an original post on ${platform} about something you find interesting, in character as ${name}.`;
|
|
423
|
+
case "seeded_topic": return `Create a post on ${platform} about: ${step.topic}. ${step.angle ? `Angle: ${step.angle}.` : ""} Make it natural and in-character as ${name}.`;
|
|
424
|
+
default: return `Perform ${step.name} on ${platform} as ${name}.`;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
buildActionEntry(name, description, basePath = "/") {
|
|
428
|
+
return {
|
|
429
|
+
id: name,
|
|
430
|
+
path: joinURL(basePath, ".actions", name),
|
|
431
|
+
summary: name,
|
|
432
|
+
meta: {
|
|
433
|
+
kind: "afs:executable",
|
|
434
|
+
name,
|
|
435
|
+
description
|
|
436
|
+
},
|
|
437
|
+
actions: [{
|
|
438
|
+
name,
|
|
439
|
+
description
|
|
440
|
+
}]
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
async listRoot(_ctx) {
|
|
444
|
+
return { data: [{
|
|
445
|
+
id: "persona:characters",
|
|
446
|
+
path: "/characters",
|
|
447
|
+
meta: {
|
|
448
|
+
kind: "persona:collection",
|
|
449
|
+
childrenCount: (await this.getCharacterNames()).length
|
|
450
|
+
}
|
|
451
|
+
}] };
|
|
452
|
+
}
|
|
453
|
+
async listCharacters(_ctx) {
|
|
454
|
+
return { data: (await this.getCharacterNames()).map((name) => ({
|
|
455
|
+
id: `persona:character:${name}`,
|
|
456
|
+
path: joinURL("/characters", name),
|
|
457
|
+
meta: {
|
|
458
|
+
kind: "persona:character",
|
|
459
|
+
childrenCount: -1
|
|
460
|
+
}
|
|
461
|
+
})) };
|
|
462
|
+
}
|
|
463
|
+
async readRoot(_ctx) {
|
|
464
|
+
return {
|
|
465
|
+
id: "persona:root",
|
|
466
|
+
path: "/",
|
|
467
|
+
content: {
|
|
468
|
+
type: "persona-provider",
|
|
469
|
+
characterCount: (await this.getCharacterNames()).length
|
|
470
|
+
},
|
|
471
|
+
meta: {
|
|
472
|
+
kind: "persona:root",
|
|
473
|
+
childrenCount: 1
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
async readCharacters(_ctx) {
|
|
478
|
+
const characterNames = await this.getCharacterNames();
|
|
479
|
+
return {
|
|
480
|
+
id: "persona:characters",
|
|
481
|
+
path: "/characters",
|
|
482
|
+
content: {
|
|
483
|
+
type: "directory",
|
|
484
|
+
count: characterNames.length
|
|
485
|
+
},
|
|
486
|
+
meta: {
|
|
487
|
+
kind: "persona:collection",
|
|
488
|
+
childrenCount: characterNames.length
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
async readCharacter(ctx) {
|
|
493
|
+
const name = ctx.params?.name;
|
|
494
|
+
const profile = this.readCharacterProfile(name);
|
|
495
|
+
if (!profile) throw new Error(`Character not found: ${name}`);
|
|
496
|
+
return {
|
|
497
|
+
id: `persona:character:${name}`,
|
|
498
|
+
path: joinURL("/characters", name),
|
|
499
|
+
content: profile,
|
|
500
|
+
meta: {
|
|
501
|
+
kind: "persona:character",
|
|
502
|
+
childrenCount: -1
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
async metaRoot(_ctx) {
|
|
507
|
+
return {
|
|
508
|
+
id: "persona:root",
|
|
509
|
+
path: "/.meta",
|
|
510
|
+
content: {
|
|
511
|
+
characterCount: (await this.getCharacterNames()).length,
|
|
512
|
+
provider: "persona"
|
|
513
|
+
},
|
|
514
|
+
meta: {
|
|
515
|
+
kind: "persona:root",
|
|
516
|
+
childrenCount: 1
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
async metaCharacters(_ctx) {
|
|
521
|
+
const characterNames = await this.getCharacterNames();
|
|
522
|
+
return {
|
|
523
|
+
id: "persona:characters",
|
|
524
|
+
path: "/characters/.meta",
|
|
525
|
+
content: {
|
|
526
|
+
type: "collection",
|
|
527
|
+
count: characterNames.length
|
|
528
|
+
},
|
|
529
|
+
meta: {
|
|
530
|
+
kind: "persona:collection",
|
|
531
|
+
childrenCount: characterNames.length
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
async readCapabilities(_ctx) {
|
|
536
|
+
return {
|
|
537
|
+
id: "/.meta/.capabilities",
|
|
538
|
+
path: "/.meta/.capabilities",
|
|
539
|
+
content: {
|
|
540
|
+
schemaVersion: 1,
|
|
541
|
+
provider: "persona",
|
|
542
|
+
description: "Virtual persona management — character lifecycle, session scheduling, social media automation",
|
|
543
|
+
tools: [],
|
|
544
|
+
actions: [{
|
|
545
|
+
kind: "persona:root",
|
|
546
|
+
description: "Root-level persona management actions",
|
|
547
|
+
catalog: [
|
|
548
|
+
{
|
|
549
|
+
name: "create-character",
|
|
550
|
+
description: "Create a new character workspace",
|
|
551
|
+
inputSchema: {
|
|
552
|
+
type: "object",
|
|
553
|
+
properties: {
|
|
554
|
+
name: {
|
|
555
|
+
type: "string",
|
|
556
|
+
description: "Character name"
|
|
557
|
+
},
|
|
558
|
+
profile: {
|
|
559
|
+
type: "object",
|
|
560
|
+
description: "Character profile data"
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
required: ["name", "profile"]
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
name: "delete-character",
|
|
568
|
+
description: "Remove a character and optionally archive",
|
|
569
|
+
inputSchema: {
|
|
570
|
+
type: "object",
|
|
571
|
+
properties: {
|
|
572
|
+
name: { type: "string" },
|
|
573
|
+
archive: { type: "boolean" }
|
|
574
|
+
},
|
|
575
|
+
required: ["name"]
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
name: "tick",
|
|
580
|
+
description: "Check and run due sessions",
|
|
581
|
+
inputSchema: {
|
|
582
|
+
type: "object",
|
|
583
|
+
properties: { character: {
|
|
584
|
+
type: "string",
|
|
585
|
+
description: "Optional: tick single character"
|
|
586
|
+
} }
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
name: "dispatch-task",
|
|
591
|
+
description: "Seed topic into characters' feeds",
|
|
592
|
+
inputSchema: {
|
|
593
|
+
type: "object",
|
|
594
|
+
properties: {
|
|
595
|
+
topic: { type: "string" },
|
|
596
|
+
goal: { type: "string" }
|
|
597
|
+
},
|
|
598
|
+
required: ["topic", "goal"]
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
],
|
|
602
|
+
discovery: {
|
|
603
|
+
pathTemplate: "/.actions",
|
|
604
|
+
note: "Root actions for persona management"
|
|
605
|
+
}
|
|
606
|
+
}],
|
|
607
|
+
operations: {
|
|
608
|
+
list: true,
|
|
609
|
+
read: true,
|
|
610
|
+
write: false,
|
|
611
|
+
delete: false,
|
|
612
|
+
search: false,
|
|
613
|
+
exec: true,
|
|
614
|
+
stat: true,
|
|
615
|
+
explain: true
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
meta: { kind: "afs:capabilities" }
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
async statRoot(_ctx) {
|
|
622
|
+
return { data: {
|
|
623
|
+
id: "persona:root",
|
|
624
|
+
path: "/",
|
|
625
|
+
meta: {
|
|
626
|
+
kind: "persona:root",
|
|
627
|
+
childrenCount: 1
|
|
628
|
+
}
|
|
629
|
+
} };
|
|
630
|
+
}
|
|
631
|
+
async statCharacters(_ctx) {
|
|
632
|
+
return { data: {
|
|
633
|
+
id: "persona:characters",
|
|
634
|
+
path: "/characters",
|
|
635
|
+
meta: {
|
|
636
|
+
kind: "persona:collection",
|
|
637
|
+
childrenCount: (await this.getCharacterNames()).length
|
|
638
|
+
}
|
|
639
|
+
} };
|
|
640
|
+
}
|
|
641
|
+
async explainRoot(_ctx) {
|
|
642
|
+
return {
|
|
643
|
+
format: "markdown",
|
|
644
|
+
content: [
|
|
645
|
+
"# Persona Provider",
|
|
646
|
+
"",
|
|
647
|
+
"Manages virtual characters that autonomously interact on social platforms.",
|
|
648
|
+
"",
|
|
649
|
+
"## Actions",
|
|
650
|
+
"- `create-character` — Create a new character workspace",
|
|
651
|
+
"- `delete-character` — Remove a character and optionally archive",
|
|
652
|
+
"- `tick` — Check and run due sessions",
|
|
653
|
+
"- `dispatch-task` — Seed topic into characters' feeds",
|
|
654
|
+
"",
|
|
655
|
+
"## Structure",
|
|
656
|
+
"- `/characters/` — Character workspaces"
|
|
657
|
+
].join("\n")
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
async listRootActions(_ctx) {
|
|
661
|
+
return { data: [
|
|
662
|
+
this.buildActionEntry("create-character", "Create a new character workspace"),
|
|
663
|
+
this.buildActionEntry("delete-character", "Remove a character and optionally archive"),
|
|
664
|
+
this.buildActionEntry("tick", "Check and run due sessions"),
|
|
665
|
+
this.buildActionEntry("dispatch-task", "Seed topic into characters' feeds")
|
|
666
|
+
] };
|
|
667
|
+
}
|
|
668
|
+
async execCreateCharacter(_ctx, args) {
|
|
669
|
+
const nameResult = validateCharacterName(args.name);
|
|
670
|
+
if (!nameResult.valid) return {
|
|
671
|
+
success: false,
|
|
672
|
+
error: {
|
|
673
|
+
code: "INVALID_NAME",
|
|
674
|
+
message: nameResult.reason
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
const name = args.name;
|
|
678
|
+
const charDir = this.characterDir(name);
|
|
679
|
+
if (existsSync(charDir)) return {
|
|
680
|
+
success: false,
|
|
681
|
+
error: {
|
|
682
|
+
code: "ALREADY_EXISTS",
|
|
683
|
+
message: `Character '${name}' already exists`
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
mkdirSync(charDir, { recursive: true });
|
|
687
|
+
mkdirSync(resolve(charDir, "browser"), { recursive: true });
|
|
688
|
+
mkdirSync(resolve(charDir, "data"), { recursive: true });
|
|
689
|
+
const profileInput = args.profile || {};
|
|
690
|
+
const profile = {
|
|
691
|
+
name,
|
|
692
|
+
description: profileInput.description || "",
|
|
693
|
+
model: profileInput.model || void 0,
|
|
694
|
+
timezone: profileInput.timezone || "UTC",
|
|
695
|
+
gap: profileInput.gap || [30, 240],
|
|
696
|
+
platforms: profileInput.platforms || ["x"],
|
|
697
|
+
interests: profileInput.interests || [],
|
|
698
|
+
personality: profileInput.personality || {
|
|
699
|
+
tone: "neutral",
|
|
700
|
+
style: "concise",
|
|
701
|
+
language: "en"
|
|
702
|
+
},
|
|
703
|
+
reply_policy: profileInput.reply_policy || {
|
|
704
|
+
enabled: true,
|
|
705
|
+
max_daily: 10
|
|
706
|
+
},
|
|
707
|
+
status: "active"
|
|
708
|
+
};
|
|
709
|
+
this.writeCharacterProfile(name, profile);
|
|
710
|
+
writeFileSync(resolve(charDir, ".afs-workspace.toml"), WORKSPACE_TOML_TEMPLATE, "utf-8");
|
|
711
|
+
return {
|
|
712
|
+
success: true,
|
|
713
|
+
data: {
|
|
714
|
+
name,
|
|
715
|
+
path: joinURL("/characters", name)
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
async execDeleteCharacter(_ctx, args) {
|
|
720
|
+
const nameResult = validateCharacterName(args.name);
|
|
721
|
+
if (!nameResult.valid) return {
|
|
722
|
+
success: false,
|
|
723
|
+
error: {
|
|
724
|
+
code: "INVALID_NAME",
|
|
725
|
+
message: nameResult.reason
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
const name = args.name;
|
|
729
|
+
const charDir = this.characterDir(name);
|
|
730
|
+
if (!existsSync(charDir)) return {
|
|
731
|
+
success: false,
|
|
732
|
+
error: {
|
|
733
|
+
code: "NOT_FOUND",
|
|
734
|
+
message: `Character '${name}' not found`
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
if (args.archive) {
|
|
738
|
+
const archiveDir = resolve(this.dataDir, "archive");
|
|
739
|
+
if (!existsSync(archiveDir)) mkdirSync(archiveDir, { recursive: true });
|
|
740
|
+
renameSync(charDir, resolve(archiveDir, name));
|
|
741
|
+
} else rmSync(charDir, {
|
|
742
|
+
recursive: true,
|
|
743
|
+
force: true
|
|
744
|
+
});
|
|
745
|
+
return { success: true };
|
|
746
|
+
}
|
|
747
|
+
async execTick(_ctx, args) {
|
|
748
|
+
const characterFilter = args.character;
|
|
749
|
+
if (characterFilter) {
|
|
750
|
+
if (!this.readCharacterProfile(characterFilter)) return {
|
|
751
|
+
success: false,
|
|
752
|
+
error: {
|
|
753
|
+
code: "NOT_FOUND",
|
|
754
|
+
message: `Character '${characterFilter}' not found`
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
return {
|
|
760
|
+
success: true,
|
|
761
|
+
data: await this.runTick(characterFilter)
|
|
762
|
+
};
|
|
763
|
+
} catch (e) {
|
|
764
|
+
return {
|
|
765
|
+
success: false,
|
|
766
|
+
error: {
|
|
767
|
+
code: "TICK_ERROR",
|
|
768
|
+
message: e instanceof Error ? e.message : "Unknown error"
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
async execDispatchTask(_ctx, args) {
|
|
774
|
+
const topic = args.topic;
|
|
775
|
+
if (!topic) return {
|
|
776
|
+
success: false,
|
|
777
|
+
error: {
|
|
778
|
+
code: "INVALID_INPUT",
|
|
779
|
+
message: "topic is required"
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
const goal = args.goal;
|
|
783
|
+
const selectCount = args.select_count;
|
|
784
|
+
const material = args.material;
|
|
785
|
+
const characterNames = await this.getCharacterNames();
|
|
786
|
+
const activeChars = [];
|
|
787
|
+
for (const name of characterNames) {
|
|
788
|
+
const profile = this.readCharacterProfile(name);
|
|
789
|
+
if (!profile || profile.status === "paused") continue;
|
|
790
|
+
const interests = profile.interests || [];
|
|
791
|
+
activeChars.push({
|
|
792
|
+
name,
|
|
793
|
+
interests
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
const topicLower = topic.toLowerCase();
|
|
797
|
+
const matched = activeChars.filter((char) => char.interests.some((i) => topicLower.includes(i.topic.toLowerCase())));
|
|
798
|
+
let selected = matched;
|
|
799
|
+
if (selectCount !== void 0 && selectCount < matched.length) selected = [...matched].sort(() => Math.random() - .5).slice(0, selectCount);
|
|
800
|
+
if (selected.length === 0) return {
|
|
801
|
+
success: true,
|
|
802
|
+
data: {
|
|
803
|
+
assigned: [],
|
|
804
|
+
angles: {},
|
|
805
|
+
warning: "No characters matched the given topic"
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
809
|
+
const angles = {};
|
|
810
|
+
for (const char of selected) {
|
|
811
|
+
const matchingInterest = char.interests.find((i) => topicLower.includes(i.topic.toLowerCase()));
|
|
812
|
+
angles[char.name] = `From ${matchingInterest?.topic || "general"} perspective on ${topic}`;
|
|
813
|
+
}
|
|
814
|
+
const db = this.getDB();
|
|
815
|
+
const assigned = selected.map((c) => c.name);
|
|
816
|
+
db.insertTask({
|
|
817
|
+
id: taskId,
|
|
818
|
+
topic,
|
|
819
|
+
goal: goal ?? null,
|
|
820
|
+
material: material ?? null,
|
|
821
|
+
assignments: Object.fromEntries(assigned.map((n) => [n, angles[n]]))
|
|
822
|
+
});
|
|
823
|
+
for (const name of assigned) db.insertSeededTopic({
|
|
824
|
+
character: name,
|
|
825
|
+
task_id: taskId,
|
|
826
|
+
topic,
|
|
827
|
+
angle: angles[name]
|
|
828
|
+
});
|
|
829
|
+
return {
|
|
830
|
+
success: true,
|
|
831
|
+
data: {
|
|
832
|
+
task_id: taskId,
|
|
833
|
+
assigned,
|
|
834
|
+
angles
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
async listCharacterActions(ctx) {
|
|
839
|
+
const name = ctx.params?.name;
|
|
840
|
+
const basePath = joinURL("/characters", name);
|
|
841
|
+
return { data: [
|
|
842
|
+
this.buildActionEntry("session", "Run one browsing session", basePath),
|
|
843
|
+
this.buildActionEntry("post", "Manual post trigger", basePath),
|
|
844
|
+
this.buildActionEntry("reply", "Manual reply trigger", basePath),
|
|
845
|
+
this.buildActionEntry("pause", "Pause character scheduling", basePath),
|
|
846
|
+
this.buildActionEntry("resume", "Resume character scheduling", basePath)
|
|
847
|
+
] };
|
|
848
|
+
}
|
|
849
|
+
async execPause(ctx, _args) {
|
|
850
|
+
const name = ctx.params?.name;
|
|
851
|
+
const profile = this.readCharacterProfile(name);
|
|
852
|
+
if (!profile) return {
|
|
853
|
+
success: false,
|
|
854
|
+
error: {
|
|
855
|
+
code: "NOT_FOUND",
|
|
856
|
+
message: `Character '${name}' not found`
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
profile.status = "paused";
|
|
860
|
+
this.writeCharacterProfile(name, profile);
|
|
861
|
+
return {
|
|
862
|
+
success: true,
|
|
863
|
+
data: {
|
|
864
|
+
name,
|
|
865
|
+
status: "paused"
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
async execResume(ctx, _args) {
|
|
870
|
+
const name = ctx.params?.name;
|
|
871
|
+
const profile = this.readCharacterProfile(name);
|
|
872
|
+
if (!profile) return {
|
|
873
|
+
success: false,
|
|
874
|
+
error: {
|
|
875
|
+
code: "NOT_FOUND",
|
|
876
|
+
message: `Character '${name}' not found`
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
profile.status = "active";
|
|
880
|
+
this.writeCharacterProfile(name, profile);
|
|
881
|
+
return {
|
|
882
|
+
success: true,
|
|
883
|
+
data: {
|
|
884
|
+
name,
|
|
885
|
+
status: "active"
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
async execSession(ctx, _args) {
|
|
890
|
+
const name = ctx.params?.name;
|
|
891
|
+
const profile = this.readCharacterProfile(name);
|
|
892
|
+
if (!profile) return {
|
|
893
|
+
success: false,
|
|
894
|
+
error: {
|
|
895
|
+
code: "NOT_FOUND",
|
|
896
|
+
message: `Character '${name}' not found`
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
if (!this.afs) return {
|
|
900
|
+
success: false,
|
|
901
|
+
error: {
|
|
902
|
+
code: "NOT_MOUNTED",
|
|
903
|
+
message: "Provider not mounted to AFS"
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
const db = this.getDB();
|
|
907
|
+
const sessionId = db.insertSession(name);
|
|
908
|
+
try {
|
|
909
|
+
const result = await this.runSessionSteps(name, profile, sessionId);
|
|
910
|
+
db.updateSession(sessionId, {
|
|
911
|
+
status: "done",
|
|
912
|
+
ended_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
913
|
+
});
|
|
914
|
+
return {
|
|
915
|
+
success: true,
|
|
916
|
+
data: result
|
|
917
|
+
};
|
|
918
|
+
} catch {
|
|
919
|
+
db.updateSession(sessionId, {
|
|
920
|
+
status: "failed",
|
|
921
|
+
ended_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
922
|
+
});
|
|
923
|
+
return {
|
|
924
|
+
success: false,
|
|
925
|
+
error: {
|
|
926
|
+
code: "SESSION_FAILED",
|
|
927
|
+
message: `Session failed for character '${name}'`
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
async execPost(_ctx, _args) {
|
|
933
|
+
return {
|
|
934
|
+
success: false,
|
|
935
|
+
error: {
|
|
936
|
+
code: "NOT_IMPLEMENTED",
|
|
937
|
+
message: "Not implemented yet"
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
async execReply(_ctx, _args) {
|
|
942
|
+
return {
|
|
943
|
+
success: false,
|
|
944
|
+
error: {
|
|
945
|
+
code: "NOT_IMPLEMENTED",
|
|
946
|
+
message: "Not implemented yet"
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
destroy() {
|
|
951
|
+
this.destroyed = true;
|
|
952
|
+
if (this.tickTimer) {
|
|
953
|
+
clearTimeout(this.tickTimer);
|
|
954
|
+
this.tickTimer = void 0;
|
|
955
|
+
}
|
|
956
|
+
if (this.db) {
|
|
957
|
+
this.db.close();
|
|
958
|
+
this.db = void 0;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
__decorate([List("/")], AFSPersonaProvider.prototype, "listRoot", null);
|
|
963
|
+
__decorate([List("/characters")], AFSPersonaProvider.prototype, "listCharacters", null);
|
|
964
|
+
__decorate([Read("/")], AFSPersonaProvider.prototype, "readRoot", null);
|
|
965
|
+
__decorate([Read("/characters")], AFSPersonaProvider.prototype, "readCharacters", null);
|
|
966
|
+
__decorate([Read("/characters/:name")], AFSPersonaProvider.prototype, "readCharacter", null);
|
|
967
|
+
__decorate([Meta("/")], AFSPersonaProvider.prototype, "metaRoot", null);
|
|
968
|
+
__decorate([Meta("/characters")], AFSPersonaProvider.prototype, "metaCharacters", null);
|
|
969
|
+
__decorate([Read("/.meta/.capabilities")], AFSPersonaProvider.prototype, "readCapabilities", null);
|
|
970
|
+
__decorate([Stat("/")], AFSPersonaProvider.prototype, "statRoot", null);
|
|
971
|
+
__decorate([Stat("/characters")], AFSPersonaProvider.prototype, "statCharacters", null);
|
|
972
|
+
__decorate([Explain("/")], AFSPersonaProvider.prototype, "explainRoot", null);
|
|
973
|
+
__decorate([Actions("/")], AFSPersonaProvider.prototype, "listRootActions", null);
|
|
974
|
+
__decorate([Actions.Exec("/", "create-character")], AFSPersonaProvider.prototype, "execCreateCharacter", null);
|
|
975
|
+
__decorate([Actions.Exec("/", "delete-character")], AFSPersonaProvider.prototype, "execDeleteCharacter", null);
|
|
976
|
+
__decorate([Actions.Exec("/", "tick")], AFSPersonaProvider.prototype, "execTick", null);
|
|
977
|
+
__decorate([Actions.Exec("/", "dispatch-task")], AFSPersonaProvider.prototype, "execDispatchTask", null);
|
|
978
|
+
__decorate([Actions("/characters/:name")], AFSPersonaProvider.prototype, "listCharacterActions", null);
|
|
979
|
+
__decorate([Actions.Exec("/characters/:name", "pause")], AFSPersonaProvider.prototype, "execPause", null);
|
|
980
|
+
__decorate([Actions.Exec("/characters/:name", "resume")], AFSPersonaProvider.prototype, "execResume", null);
|
|
981
|
+
__decorate([Actions.Exec("/characters/:name", "session")], AFSPersonaProvider.prototype, "execSession", null);
|
|
982
|
+
__decorate([Actions.Exec("/characters/:name", "post")], AFSPersonaProvider.prototype, "execPost", null);
|
|
983
|
+
__decorate([Actions.Exec("/characters/:name", "reply")], AFSPersonaProvider.prototype, "execReply", null);
|
|
984
|
+
|
|
985
|
+
//#endregion
|
|
986
|
+
export { AFSPersonaProvider, PersonaDB };
|
|
987
|
+
//# sourceMappingURL=index.mjs.map
|