pi-jinyong-xia 1.0.0

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 (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +378 -0
  3. package/dist/assets/portrait-ascii/portrait-data.json +1 -0
  4. package/dist/extensions/characters.d.ts +19 -0
  5. package/dist/extensions/characters.js +52 -0
  6. package/dist/extensions/characters.js.map +1 -0
  7. package/dist/extensions/index.d.ts +1 -0
  8. package/dist/extensions/index.js +20 -0
  9. package/dist/extensions/index.js.map +1 -0
  10. package/dist/extensions/missions-mbti.d.ts +39 -0
  11. package/dist/extensions/missions-mbti.js +101 -0
  12. package/dist/extensions/missions-mbti.js.map +1 -0
  13. package/dist/extensions/missions-templates-danger.d.ts +5 -0
  14. package/dist/extensions/missions-templates-danger.js +432 -0
  15. package/dist/extensions/missions-templates-danger.js.map +1 -0
  16. package/dist/extensions/missions-templates-safe.d.ts +34 -0
  17. package/dist/extensions/missions-templates-safe.js +307 -0
  18. package/dist/extensions/missions-templates-safe.js.map +1 -0
  19. package/dist/extensions/missions-templates.d.ts +12 -0
  20. package/dist/extensions/missions-templates.js +26 -0
  21. package/dist/extensions/missions-templates.js.map +1 -0
  22. package/dist/extensions/missions.d.ts +27 -0
  23. package/dist/extensions/missions.js +81 -0
  24. package/dist/extensions/missions.js.map +1 -0
  25. package/dist/extensions/state.d.ts +89 -0
  26. package/dist/extensions/state.js +205 -0
  27. package/dist/extensions/state.js.map +1 -0
  28. package/dist/extensions/telemetry.d.ts +179 -0
  29. package/dist/extensions/telemetry.js +241 -0
  30. package/dist/extensions/telemetry.js.map +1 -0
  31. package/dist/extensions/wuxue-achievements.d.ts +27 -0
  32. package/dist/extensions/wuxue-achievements.js +92 -0
  33. package/dist/extensions/wuxue-achievements.js.map +1 -0
  34. package/dist/extensions/wuxue-boss.d.ts +15 -0
  35. package/dist/extensions/wuxue-boss.js +405 -0
  36. package/dist/extensions/wuxue-boss.js.map +1 -0
  37. package/dist/extensions/wuxue-data.d.ts +35 -0
  38. package/dist/extensions/wuxue-data.js +409 -0
  39. package/dist/extensions/wuxue-data.js.map +1 -0
  40. package/dist/extensions/wuxue-encounter.d.ts +23 -0
  41. package/dist/extensions/wuxue-encounter.js +368 -0
  42. package/dist/extensions/wuxue-encounter.js.map +1 -0
  43. package/dist/extensions/wuxue-faction.d.ts +43 -0
  44. package/dist/extensions/wuxue-faction.js +182 -0
  45. package/dist/extensions/wuxue-faction.js.map +1 -0
  46. package/dist/extensions/wuxue-profession.d.ts +33 -0
  47. package/dist/extensions/wuxue-profession.js +117 -0
  48. package/dist/extensions/wuxue-profession.js.map +1 -0
  49. package/dist/extensions/wuxue-talent.d.ts +41 -0
  50. package/dist/extensions/wuxue-talent.js +209 -0
  51. package/dist/extensions/wuxue-talent.js.map +1 -0
  52. package/dist/extensions/wuxue-types.d.ts +155 -0
  53. package/dist/extensions/wuxue-types.js +28 -0
  54. package/dist/extensions/wuxue-types.js.map +1 -0
  55. package/dist/extensions/wuxue.d.ts +89 -0
  56. package/dist/extensions/wuxue.js +441 -0
  57. package/dist/extensions/wuxue.js.map +1 -0
  58. package/dist/extensions/xia-boss.d.ts +14 -0
  59. package/dist/extensions/xia-boss.js +337 -0
  60. package/dist/extensions/xia-boss.js.map +1 -0
  61. package/dist/extensions/xia-cmd-faction.d.ts +4 -0
  62. package/dist/extensions/xia-cmd-faction.js +313 -0
  63. package/dist/extensions/xia-cmd-faction.js.map +1 -0
  64. package/dist/extensions/xia-cmd-missions.d.ts +4 -0
  65. package/dist/extensions/xia-cmd-missions.js +232 -0
  66. package/dist/extensions/xia-cmd-missions.js.map +1 -0
  67. package/dist/extensions/xia-cmd-shop-rank.d.ts +4 -0
  68. package/dist/extensions/xia-cmd-shop-rank.js +155 -0
  69. package/dist/extensions/xia-cmd-shop-rank.js.map +1 -0
  70. package/dist/extensions/xia-cmd-stats.d.ts +4 -0
  71. package/dist/extensions/xia-cmd-stats.js +290 -0
  72. package/dist/extensions/xia-cmd-stats.js.map +1 -0
  73. package/dist/extensions/xia-commands.d.ts +19 -0
  74. package/dist/extensions/xia-commands.js +138 -0
  75. package/dist/extensions/xia-commands.js.map +1 -0
  76. package/dist/extensions/xia-events.d.ts +58 -0
  77. package/dist/extensions/xia-events.js +615 -0
  78. package/dist/extensions/xia-events.js.map +1 -0
  79. package/dist/extensions/xia-render.d.ts +65 -0
  80. package/dist/extensions/xia-render.js +490 -0
  81. package/dist/extensions/xia-render.js.map +1 -0
  82. package/dist/extensions/xia-select.d.ts +24 -0
  83. package/dist/extensions/xia-select.js +413 -0
  84. package/dist/extensions/xia-select.js.map +1 -0
  85. package/dist/extensions/xia.d.ts +14 -0
  86. package/dist/extensions/xia.js +145 -0
  87. package/dist/extensions/xia.js.map +1 -0
  88. package/entry.js +18 -0
  89. package/package.json +61 -0
  90. package/skills/SKILL.md +83 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * 状态持久化
3
+ *
4
+ * 保存/加载侠客状态到 ~/.pi/agent/jinyong-xia-state.json
5
+ */
6
+ import type { WuxueState } from "./wuxue.js";
7
+ /** 已拥有的武功 */
8
+ export interface OwnedSkill {
9
+ id: string;
10
+ level: number;
11
+ xp: number;
12
+ }
13
+ /** 任务风险等级 */
14
+ export type RiskLevel = "safe" | "low" | "medium" | "high" | "extreme";
15
+ /** 任务状态 */
16
+ export type MissionStatus = "available" | "active" | "completed" | "failed" | "abandoned";
17
+ /** 任务记录 */
18
+ export interface MissionRecord {
19
+ id: string;
20
+ templateId: string;
21
+ name: string;
22
+ description: string;
23
+ risk: RiskLevel;
24
+ status: MissionStatus;
25
+ goldReward: number;
26
+ xpReward: number;
27
+ hpCost: number;
28
+ startedAt: number;
29
+ completedAt: number | null;
30
+ requiredPlanSteps: number;
31
+ completedPlanSteps: number;
32
+ planChoices: string[];
33
+ }
34
+ /** 任务偏好追踪 */
35
+ export interface MissionPreferences {
36
+ totalCompleted: number;
37
+ totalFailed: number;
38
+ riskProfile: {
39
+ safe: number;
40
+ low: number;
41
+ medium: number;
42
+ high: number;
43
+ extreme: number;
44
+ };
45
+ favoriteType: string | null;
46
+ lastMissionAt: number;
47
+ }
48
+ export interface PetState {
49
+ version: number;
50
+ userId: string;
51
+ telemetryEnabled: boolean;
52
+ characterId: string | null;
53
+ nickname: string | null;
54
+ specialSkill: string | null;
55
+ specialSkillLevel: number;
56
+ specialSkillXp: number;
57
+ martialSkills: OwnedSkill[];
58
+ hidden: boolean;
59
+ weapon: string | null;
60
+ ownedWeapons: string[];
61
+ wuxue: WuxueState;
62
+ createdAt: number;
63
+ lastActiveAt: number;
64
+ SignPublicKey: string | null;
65
+ SignPrivateKey: string | null;
66
+ email: string | null;
67
+ missions: MissionRecord[];
68
+ missionPreferences: MissionPreferences;
69
+ factionId: string | null;
70
+ factionContribution: number;
71
+ joinedFactionAt: number | null;
72
+ professionId: string | null;
73
+ joinedProfessionAt: number | null;
74
+ talents: string[];
75
+ talentPoints: number;
76
+ lastTelemetryUpload: number;
77
+ }
78
+ /** Generate a stable OS-based user ID */
79
+ export declare function generateUserId(): string;
80
+ export declare function createInitialPetState(): PetState;
81
+ /** Generate Ed25519 key pair for payload signing */
82
+ export declare function generateSigningKeys(): {
83
+ publicKey: string;
84
+ privateKey: string;
85
+ };
86
+ /** Ensure signing keys exist, generating if needed */
87
+ export declare function ensureSigningKeys(state: PetState): void;
88
+ export declare function loadState(): PetState;
89
+ export declare function saveState(state: PetState): void;
@@ -0,0 +1,205 @@
1
+ /**
2
+ * 状态持久化
3
+ *
4
+ * 保存/加载侠客状态到 ~/.pi/agent/jinyong-xia-state.json
5
+ */
6
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
7
+ import { resolve, dirname } from "node:path";
8
+ import { homedir, hostname, userInfo, platform, arch } from "node:os";
9
+ import { createHash, generateKeyPairSync } from "node:crypto";
10
+ import { createInitialState, migrateSkills, getSkill } from "./wuxue.js";
11
+ // ═══════════════════════════════════════════════════════════════════════════
12
+ // STATE FILE
13
+ // ═══════════════════════════════════════════════════════════════════════════
14
+ const STATE_VERSION = 9;
15
+ /** Generate a stable OS-based user ID */
16
+ export function generateUserId() {
17
+ try {
18
+ const raw = `${hostname()}:${userInfo().username}:${platform()}:${arch()}`;
19
+ return createHash("sha256").update(raw).digest("hex").substring(0, 16);
20
+ }
21
+ catch {
22
+ return createHash("sha256").update(`unknown:${Date.now()}`).digest("hex").substring(0, 16);
23
+ }
24
+ }
25
+ function getStatePath() {
26
+ return resolve(homedir(), ".pi", "agent", "jinyong-xia-state.json");
27
+ }
28
+ function ensureDir(filePath) {
29
+ const dir = dirname(filePath);
30
+ if (!existsSync(dir)) {
31
+ mkdirSync(dir, { recursive: true });
32
+ }
33
+ }
34
+ export function createInitialPetState() {
35
+ return {
36
+ version: STATE_VERSION,
37
+ userId: generateUserId(),
38
+ telemetryEnabled: false,
39
+ characterId: null,
40
+ nickname: null,
41
+ specialSkill: null,
42
+ specialSkillLevel: 1,
43
+ specialSkillXp: 0,
44
+ martialSkills: [],
45
+ hidden: false,
46
+ weapon: "mu-jian",
47
+ ownedWeapons: ["mu-jian"],
48
+ wuxue: createInitialState(),
49
+ createdAt: Date.now(),
50
+ lastActiveAt: Date.now(),
51
+ SignPublicKey: null,
52
+ SignPrivateKey: null,
53
+ email: null,
54
+ missions: [],
55
+ missionPreferences: {
56
+ totalCompleted: 0,
57
+ totalFailed: 0,
58
+ riskProfile: { safe: 0, low: 0, medium: 0, high: 0, extreme: 0 },
59
+ favoriteType: null,
60
+ lastMissionAt: 0,
61
+ },
62
+ // 帮派
63
+ factionId: null,
64
+ factionContribution: 0,
65
+ joinedFactionAt: null,
66
+ // 职业
67
+ professionId: null,
68
+ joinedProfessionAt: null,
69
+ // 天赋
70
+ talents: [],
71
+ talentPoints: 0,
72
+ lastTelemetryUpload: 0,
73
+ };
74
+ }
75
+ /** Generate Ed25519 key pair for payload signing */
76
+ export function generateSigningKeys() {
77
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
78
+ // Export as base64 for JSON serialization
79
+ const pubB64 = publicKey.export({ type: "spki", format: "der" }).toString("base64");
80
+ const privB64 = privateKey.export({ type: "pkcs8", format: "der" }).toString("base64");
81
+ return { publicKey: pubB64, privateKey: privB64 };
82
+ }
83
+ /** Ensure signing keys exist, generating if needed */
84
+ export function ensureSigningKeys(state) {
85
+ if (state.SignPublicKey && state.SignPrivateKey)
86
+ return;
87
+ const keys = generateSigningKeys();
88
+ state.SignPublicKey = keys.publicKey;
89
+ state.SignPrivateKey = keys.privateKey;
90
+ }
91
+ export function loadState() {
92
+ const path = getStatePath();
93
+ if (!existsSync(path)) {
94
+ return createInitialPetState();
95
+ }
96
+ try {
97
+ const raw = readFileSync(path, "utf8");
98
+ const state = JSON.parse(raw);
99
+ // Migration chain
100
+ if (!state.version || state.version < STATE_VERSION) {
101
+ const migrated = state;
102
+ delete migrated.mbti;
103
+ delete migrated.mbtiScores;
104
+ delete migrated.conversationTraits;
105
+ // v1→v2: weapons
106
+ if (!state.weapon)
107
+ state.weapon = "mu-jian";
108
+ if (!state.ownedWeapons)
109
+ state.ownedWeapons = ["mu-jian"];
110
+ if (state.characterId === undefined)
111
+ state.characterId = null;
112
+ // v2→v3: 单武功→多武功
113
+ if (!state.martialSkills)
114
+ state.martialSkills = [];
115
+ // 把旧的单武功字段迁移进新数组
116
+ if (state.martialSkills.length === 0 && state.specialSkill) {
117
+ const oldId = state.specialSkill;
118
+ const oldLevel = state.specialSkillLevel ?? 1;
119
+ const oldXp = state.specialSkillXp ?? 0;
120
+ // 验证旧 id 有效
121
+ if (getSkill(oldId)) {
122
+ state.martialSkills.push({ id: oldId, level: oldLevel, xp: oldXp });
123
+ }
124
+ }
125
+ // 清理旧字段(保留以兼容,但不再使用)
126
+ // 验证所有武功 id 有效
127
+ state.martialSkills = state.martialSkills.filter((s) => getSkill(s.id));
128
+ // v3→v4: 血量与体力分离
129
+ if (state.wuxue) {
130
+ if (!('hp' in state.wuxue) || typeof state.wuxue.hp !== 'number' || isNaN(state.wuxue.hp)) {
131
+ state.wuxue.hp = 100 + (state.wuxue.level - 1) * 10;
132
+ state.wuxue.maxHp = 100 + (state.wuxue.level - 1) * 10;
133
+ }
134
+ if (typeof state.wuxue.maxHp !== 'number' || isNaN(state.wuxue.maxHp) || state.wuxue.maxHp <= 0) {
135
+ state.wuxue.maxHp = 100 + (state.wuxue.level - 1) * 10;
136
+ state.wuxue.hp = state.wuxue.maxHp;
137
+ }
138
+ }
139
+ // 清理旧单武功残留字段
140
+ delete state.specialSkill;
141
+ delete state.specialSkillLevel;
142
+ delete state.specialSkillXp;
143
+ // v5→v6: userId and telemetry
144
+ if (!state.userId)
145
+ state.userId = generateUserId();
146
+ if (state.telemetryEnabled === undefined)
147
+ state.telemetryEnabled = false;
148
+ // v6→v7: Ed25519 signing keys
149
+ if (!state.SignPublicKey) {
150
+ const keys = generateSigningKeys();
151
+ state.SignPublicKey = keys.publicKey;
152
+ state.SignPrivateKey = keys.privateKey;
153
+ }
154
+ // v7→v8: email, missions, mission preferences
155
+ if (!state.email)
156
+ state.email = null;
157
+ if (!state.missions)
158
+ state.missions = [];
159
+ if (!state.missionPreferences)
160
+ state.missionPreferences = {
161
+ totalCompleted: 0, totalFailed: 0,
162
+ riskProfile: { safe: 0, low: 0, medium: 0, high: 0, extreme: 0 },
163
+ favoriteType: null, lastMissionAt: 0,
164
+ };
165
+ // v8→v9: faction, profession, talents
166
+ if (state.factionId === undefined)
167
+ state.factionId = null;
168
+ if (state.factionContribution === undefined)
169
+ state.factionContribution = 0;
170
+ if (state.joinedFactionAt === undefined)
171
+ state.joinedFactionAt = null;
172
+ if (state.professionId === undefined)
173
+ state.professionId = null;
174
+ if (state.joinedProfessionAt === undefined)
175
+ state.joinedProfessionAt = null;
176
+ if (!Array.isArray(state.talents))
177
+ state.talents = [];
178
+ if (state.talentPoints === undefined)
179
+ state.talentPoints = 0;
180
+ state.version = STATE_VERSION;
181
+ }
182
+ // Ensure wuxue has all fields (forward compat)
183
+ if (!state.wuxue)
184
+ state.wuxue = createInitialState();
185
+ const fresh = createInitialState();
186
+ for (const key of Object.keys(fresh)) {
187
+ if (state.wuxue[key] === undefined) {
188
+ state.wuxue[key] = fresh[key];
189
+ }
190
+ }
191
+ // 补齐老存档 skills 缺失的 element 等字段
192
+ migrateSkills(state.wuxue.skills);
193
+ return state;
194
+ }
195
+ catch {
196
+ return createInitialPetState();
197
+ }
198
+ }
199
+ export function saveState(state) {
200
+ state.lastActiveAt = Date.now();
201
+ const path = getStatePath();
202
+ ensureDir(path);
203
+ writeFileSync(path, JSON.stringify(state, null, 2), "utf8");
204
+ }
205
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../extensions/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * 遥测上传模块 v2 — Cloudflare D1 后端
3
+ *
4
+ * 零 API Key:客户端直接 fetch 到 Worker 公开端点。
5
+ * 当 telemetryEnabled = true 时,定期上传用户游戏数据到云端。
6
+ */
7
+ import type { PetState } from "./state.js";
8
+ export interface TelemetryPayload {
9
+ userId: string;
10
+ characterId: string | null;
11
+ characterName: string | null;
12
+ level: number;
13
+ title: string;
14
+ gold: number;
15
+ totalXp: number;
16
+ totalEdits: number;
17
+ totalCommands: number;
18
+ totalTrainings: number;
19
+ martialSkills: string;
20
+ weapon: string | null;
21
+ achievements: string;
22
+ bossesDefeated: number;
23
+ createdAt: number;
24
+ lastActiveAt: number;
25
+ reportedAt: number;
26
+ version: string;
27
+ publicKey: string;
28
+ signature: string;
29
+ email: string | null;
30
+ mbtiType: string | null;
31
+ mbtiScores: string | null;
32
+ factionId: string | null;
33
+ factionContribution: number;
34
+ professionId: string | null;
35
+ talents: string;
36
+ talentPoints: number;
37
+ }
38
+ export interface LeaderboardEntry {
39
+ user_id: string;
40
+ character_name: string | null;
41
+ level: number;
42
+ title: string;
43
+ gold: number;
44
+ total_xp: number;
45
+ total_edits: number;
46
+ total_commands: number;
47
+ total_trainings: number;
48
+ martial_skills: string | null;
49
+ weapon: string | null;
50
+ achievements: string | null;
51
+ bosses_defeated: number;
52
+ last_active_at: number;
53
+ reported_at: number;
54
+ }
55
+ export interface LeaderboardResponse {
56
+ ok: boolean;
57
+ sort: string;
58
+ offset: number;
59
+ limit: number;
60
+ players: LeaderboardEntry[];
61
+ }
62
+ export interface PlayerDetail {
63
+ ok: boolean;
64
+ player: Record<string, unknown>;
65
+ rank: number;
66
+ }
67
+ export interface GlobalStats {
68
+ ok: boolean;
69
+ totalPlayers: number;
70
+ totalReports: number;
71
+ updatedAt: number;
72
+ topPlayers: Array<{
73
+ user_id: string;
74
+ character_name: string | null;
75
+ level: number;
76
+ title: string;
77
+ total_xp: number;
78
+ }>;
79
+ levelDistribution: Array<{
80
+ tier: string;
81
+ count: number;
82
+ }>;
83
+ }
84
+ /**
85
+ * 从 PetState 构建遥测上传数据
86
+ */
87
+ export declare function buildTelemetryPayload(state: PetState, getCharacter: (id: string) => {
88
+ name: string;
89
+ } | null, getLevelTitle: (level: number) => string, packageVersion: string): TelemetryPayload | null;
90
+ /**
91
+ * 从 state 恢复上次上传时间(重启后调用一次)
92
+ */
93
+ export declare function restoreLastUploadTime(state: {
94
+ lastTelemetryUpload?: number;
95
+ }): void;
96
+ /**
97
+ * 上传遥测数据到 Worker
98
+ */
99
+ export declare function uploadTelemetry(payload: TelemetryPayload): Promise<{
100
+ ok: boolean;
101
+ error?: string;
102
+ }>;
103
+ /**
104
+ * 条件上传:检查间隔后自动上传
105
+ */
106
+ export declare function maybeUpload(state: PetState, getCharacter: (id: string) => {
107
+ name: string;
108
+ } | null, getLevelTitle: (level: number) => string, packageVersion: string): Promise<{
109
+ ok: boolean;
110
+ error?: string;
111
+ }>;
112
+ /**
113
+ * 获取排行榜
114
+ */
115
+ export declare function fetchLeaderboard(sort?: "level" | "gold" | "edits" | "xp", limit?: number, offset?: number): Promise<LeaderboardResponse | null>;
116
+ /**
117
+ * 获取 MBTI 性格匹配的真人玩家
118
+ */
119
+ export declare function fetchMatch(userId: string): Promise<any>;
120
+ /**
121
+ * 获取某玩家详情 + 排名
122
+ */
123
+ export declare function fetchPlayerProfile(userId: string): Promise<PlayerDetail | null>;
124
+ /**
125
+ * 获取全局统计
126
+ */
127
+ export declare function fetchGlobalStats(): Promise<GlobalStats | null>;
128
+ export interface ServerEncounterResult {
129
+ ok: boolean;
130
+ type: string;
131
+ name: string;
132
+ description: string;
133
+ goldAmount: number;
134
+ xpAmount: number;
135
+ hpChange?: number;
136
+ skillScrollId?: string;
137
+ weaponId?: string;
138
+ weaponName?: string;
139
+ itemId?: string;
140
+ itemName?: string;
141
+ location?: string;
142
+ battleResult?: string;
143
+ encounterChar?: string;
144
+ }
145
+ export interface ServerBossFightResult {
146
+ ok: boolean;
147
+ won: boolean;
148
+ bossName: string;
149
+ bossElement: string;
150
+ goldReward: number;
151
+ xpReward: number;
152
+ skillXpReward: number;
153
+ hpChange: number;
154
+ logs: Array<{
155
+ turn: number;
156
+ attacker: string;
157
+ damage: number;
158
+ selfDamage?: number;
159
+ elementBonus?: string;
160
+ isSkillHit?: boolean;
161
+ dice?: {
162
+ event: string;
163
+ desc: string;
164
+ };
165
+ }>;
166
+ error?: string;
167
+ }
168
+ /**
169
+ * 服务端生成遭遇 — 客户端不再本地生成
170
+ */
171
+ export declare function serverEncounter(userId: string, level: number, weaponAtk: number, currentHp: number, maxHp: number): Promise<ServerEncounterResult | null>;
172
+ /**
173
+ * 服务端 Boss 战斗 — 客户端不再本地计算
174
+ */
175
+ export declare function serverBossFight(userId: string, level: number, weaponAtk: number, weaponElement: string, skillLevel: number, skillElement: string | undefined, bossId: string): Promise<ServerBossFightResult | null>;
176
+ /**
177
+ * 获取公开配置(显示用途)
178
+ */
179
+ export declare function fetchPublicConfig(): Promise<any>;