@zhin.js/plugin-ledger 1.0.1 → 1.0.3
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 +24 -0
- package/commands/ledger-add/index.js +6 -4
- package/commands/ledger-add/index.ts +6 -4
- package/commands/ledger-delete/index.js +6 -4
- package/commands/ledger-delete/index.ts +6 -4
- package/commands/ledger-list/index.js +8 -6
- package/commands/ledger-list/index.ts +8 -6
- package/commands/ledger-month/index.js +5 -3
- package/commands/ledger-month/index.ts +5 -3
- package/commands/ledger-report/index.js +11 -11
- package/commands/ledger-report/index.ts +10 -12
- package/commands/ledger-stats/index.js +8 -6
- package/commands/ledger-stats/index.ts +8 -6
- package/commands/ledger-today/index.js +5 -3
- package/commands/ledger-today/index.ts +5 -3
- package/commands/ledger-week/index.js +5 -3
- package/commands/ledger-week/index.ts +5 -3
- package/lib/config.d.ts +11 -5
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +3 -3
- package/lib/config.js.map +1 -1
- package/lib/console.d.ts +4 -0
- package/lib/console.d.ts.map +1 -0
- package/lib/console.js +39 -0
- package/lib/console.js.map +1 -0
- package/lib/entry-input.d.ts +3 -2
- package/lib/entry-input.d.ts.map +1 -1
- package/lib/entry-input.js +11 -9
- package/lib/entry-input.js.map +1 -1
- package/lib/i18n.d.ts +2 -0
- package/lib/i18n.d.ts.map +1 -0
- package/lib/i18n.js +200 -0
- package/lib/i18n.js.map +1 -0
- package/lib/identity.d.ts +3 -1
- package/lib/identity.d.ts.map +1 -1
- package/lib/identity.js +5 -2
- package/lib/identity.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -1
- package/lib/index.js.map +1 -1
- package/lib/money.d.ts +2 -1
- package/lib/money.d.ts.map +1 -1
- package/lib/money.js +6 -5
- package/lib/money.js.map +1 -1
- package/lib/period.d.ts +4 -3
- package/lib/period.d.ts.map +1 -1
- package/lib/period.js +8 -16
- package/lib/period.js.map +1 -1
- package/lib/report-format.d.ts +5 -4
- package/lib/report-format.d.ts.map +1 -1
- package/lib/report-format.js +25 -24
- package/lib/report-format.js.map +1 -1
- package/lib/service.d.ts +14 -1
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +41 -24
- package/lib/service.js.map +1 -1
- package/lib/store.d.ts +3 -1
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +8 -4
- package/lib/store.js.map +1 -1
- package/lib/tool-caller.d.ts +32 -0
- package/lib/tool-caller.d.ts.map +1 -0
- package/lib/tool-caller.js +74 -0
- package/lib/tool-caller.js.map +1 -0
- package/package.json +36 -7
- package/pages/index/index.js +63 -0
- package/pages/index/index.ts +65 -0
- package/plugin.js +3 -1
- package/schema.json +15 -0
- package/skills/ledger/SKILL.md +41 -0
- package/skills/ledger/tools/ledger_add/index.js +25 -0
- package/skills/ledger/tools/ledger_add/index.ts +26 -0
- package/skills/ledger/tools/ledger_delete/index.js +22 -0
- package/skills/ledger/tools/ledger_delete/index.ts +23 -0
- package/skills/ledger/tools/ledger_list/index.js +23 -0
- package/skills/ledger/tools/ledger_list/index.ts +24 -0
- package/skills/ledger/tools/ledger_summary/index.js +27 -0
- package/skills/ledger/tools/ledger_summary/index.ts +28 -0
- package/src/config.ts +3 -3
- package/src/console.ts +47 -0
- package/src/entry-input.ts +12 -9
- package/src/i18n.ts +200 -0
- package/src/identity.ts +6 -2
- package/src/index.ts +9 -1
- package/src/money.ts +7 -5
- package/src/period.ts +7 -13
- package/src/report-format.ts +26 -24
- package/src/service.ts +56 -24
- package/src/store.ts +9 -4
- package/src/tool-caller.ts +101 -0
package/src/store.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveLocale, type Locale } from 'zhin.js/i18n';
|
|
1
2
|
import type { DatabaseHostModel, DeliveryTarget, PluginDatabaseHost } from 'zhin.js';
|
|
2
3
|
|
|
3
4
|
export const ENTRY_TABLE = 'ledger_entries';
|
|
@@ -20,6 +21,7 @@ export interface LedgerUserRecord {
|
|
|
20
21
|
readonly ownerKey: string;
|
|
21
22
|
readonly nextSeq: number;
|
|
22
23
|
readonly reportEnabled: boolean;
|
|
24
|
+
readonly reportLocale?: Locale;
|
|
23
25
|
readonly reportTarget: DeliveryTarget | undefined;
|
|
24
26
|
readonly lastReportDate: string;
|
|
25
27
|
}
|
|
@@ -38,9 +40,10 @@ export function defineLedgerTables(database: Pick<PluginDatabaseHost, 'define'>)
|
|
|
38
40
|
owner_key: { type: 'text', nullable: false },
|
|
39
41
|
next_seq: { type: 'integer', default: 1 },
|
|
40
42
|
report_enabled: { type: 'integer', default: 0 },
|
|
43
|
+
report_locale: { type: 'text', length: 16, nullable: true },
|
|
41
44
|
report_target: { type: 'text', default: '' },
|
|
42
45
|
last_report_date: { type: 'text', default: '' },
|
|
43
|
-
});
|
|
46
|
+
}, { addColumns: ['report_locale'] });
|
|
44
47
|
database.define(PRIVATE_TARGET_TABLE, {
|
|
45
48
|
owner_key: { type: 'text', nullable: false },
|
|
46
49
|
target: { type: 'text', nullable: false },
|
|
@@ -72,6 +75,7 @@ function toUser(row: Record<string, unknown>): LedgerUserRecord {
|
|
|
72
75
|
ownerKey: String(row.owner_key),
|
|
73
76
|
nextSeq: Number(row.next_seq ?? 1),
|
|
74
77
|
reportEnabled: Number(row.report_enabled ?? 0) === 1,
|
|
78
|
+
reportLocale: resolveLocale(row.report_locale),
|
|
75
79
|
reportTarget: parseTarget(row.report_target),
|
|
76
80
|
lastReportDate: String(row.last_report_date ?? ''),
|
|
77
81
|
};
|
|
@@ -97,7 +101,7 @@ export class LedgerStore {
|
|
|
97
101
|
|
|
98
102
|
async getUser(ownerKey: string): Promise<LedgerUserRecord | undefined> {
|
|
99
103
|
const rows = await this.#model(USER_TABLE)
|
|
100
|
-
.select('owner_key', 'next_seq', 'report_enabled', 'report_target', 'last_report_date')
|
|
104
|
+
.select('owner_key', 'next_seq', 'report_enabled', 'report_locale', 'report_target', 'last_report_date')
|
|
101
105
|
.where({ owner_key: ownerKey });
|
|
102
106
|
const first = rows[0];
|
|
103
107
|
return first ? toUser(first) : undefined;
|
|
@@ -154,10 +158,11 @@ export class LedgerStore {
|
|
|
154
158
|
await this.#model(ENTRY_TABLE).delete().where({ owner_key: ownerKey, seq });
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
async setReport(ownerKey: string, enabled: boolean, target: DeliveryTarget | undefined, lastReportDate: string): Promise<void> {
|
|
161
|
+
async setReport(ownerKey: string, enabled: boolean, target: DeliveryTarget | undefined, lastReportDate: string, locale?: Locale): Promise<void> {
|
|
158
162
|
await this.#ensureUser(ownerKey);
|
|
159
163
|
await this.#model(USER_TABLE).update({
|
|
160
164
|
report_enabled: enabled ? 1 : 0,
|
|
165
|
+
...(enabled ? { report_locale: resolveLocale(locale) } : {}),
|
|
161
166
|
report_target: target ? JSON.stringify(target) : '',
|
|
162
167
|
last_report_date: lastReportDate,
|
|
163
168
|
}).where({ owner_key: ownerKey });
|
|
@@ -184,7 +189,7 @@ export class LedgerStore {
|
|
|
184
189
|
|
|
185
190
|
async listReportSubscribers(): Promise<LedgerUserRecord[]> {
|
|
186
191
|
const rows = await this.#model(USER_TABLE)
|
|
187
|
-
.select('owner_key', 'next_seq', 'report_enabled', 'report_target', 'last_report_date')
|
|
192
|
+
.select('owner_key', 'next_seq', 'report_enabled', 'report_locale', 'report_target', 'last_report_date')
|
|
188
193
|
.where({ report_enabled: 1 });
|
|
189
194
|
return rows.map(toUser);
|
|
190
195
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { resolveLocale, type Locale } from 'zhin.js/i18n';
|
|
2
|
+
import { createDeliveryTarget, type DeliveryTarget } from 'zhin.js';
|
|
3
|
+
import { adapterFeatureId, type AdapterIndex } from 'zhin.js/adapter';
|
|
4
|
+
import { messages as t } from './i18n.js';
|
|
5
|
+
import { LedgerInputError, ledgerOwnerKey, type LedgerCaller } from './identity.js';
|
|
6
|
+
|
|
7
|
+
export type LedgerToolOrigin =
|
|
8
|
+
| Readonly<{
|
|
9
|
+
kind: 'im';
|
|
10
|
+
platform: string;
|
|
11
|
+
endpoint: string;
|
|
12
|
+
scope: 'private' | 'group' | 'channel';
|
|
13
|
+
sceneId: string;
|
|
14
|
+
messageId?: string;
|
|
15
|
+
threadId?: string;
|
|
16
|
+
conversation?: DeliveryTarget & Readonly<{ name?: string }>;
|
|
17
|
+
}>
|
|
18
|
+
| Readonly<{ kind: Exclude<string, 'im'>; [key: string]: unknown }>;
|
|
19
|
+
|
|
20
|
+
export interface LedgerToolCallerContext {
|
|
21
|
+
readonly origin: LedgerToolOrigin;
|
|
22
|
+
readonly locale?: Locale;
|
|
23
|
+
readonly senderId?: string;
|
|
24
|
+
readonly principal?: Readonly<{ subjectId: string; roles: readonly string[] }>;
|
|
25
|
+
project?<T>(feature: typeof adapterFeatureId): T;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isImOrigin(
|
|
29
|
+
origin: LedgerToolOrigin,
|
|
30
|
+
): origin is Extract<LedgerToolOrigin, { readonly kind: 'im' }> {
|
|
31
|
+
return origin.kind === 'im';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolveToolConversation(context: LedgerToolCallerContext, locale: Locale): DeliveryTarget {
|
|
35
|
+
const origin = context.origin;
|
|
36
|
+
if (!isImOrigin(origin)) {
|
|
37
|
+
throw new LedgerInputError(t(locale, 'actor'));
|
|
38
|
+
}
|
|
39
|
+
let endpointId = origin.conversation?.endpoint.id ?? origin.endpoint;
|
|
40
|
+
if (typeof context.project === 'function') {
|
|
41
|
+
try {
|
|
42
|
+
const index = context.project<AdapterIndex>(adapterFeatureId);
|
|
43
|
+
if (index && typeof index.describe === 'function') {
|
|
44
|
+
const matches = index
|
|
45
|
+
.describe()
|
|
46
|
+
.filter(
|
|
47
|
+
(endpoint) =>
|
|
48
|
+
(String(endpoint.id) === origin.endpoint || endpoint.name === origin.endpoint) &&
|
|
49
|
+
index.clientAdapter(endpoint.id) === origin.platform,
|
|
50
|
+
);
|
|
51
|
+
if (matches.length === 1) {
|
|
52
|
+
endpointId = String(matches[0]!.id);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// Fall back to origin endpoint when AdapterIndex projection is unavailable in lightweight contexts.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!endpointId) {
|
|
60
|
+
throw new LedgerInputError(t(locale, 'actor'));
|
|
61
|
+
}
|
|
62
|
+
if (origin.conversation) {
|
|
63
|
+
return createDeliveryTarget({
|
|
64
|
+
conversation: {
|
|
65
|
+
...origin.conversation,
|
|
66
|
+
endpoint: { id: endpointId, adapter: origin.platform },
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return createDeliveryTarget({
|
|
71
|
+
conversation: {
|
|
72
|
+
endpoint: { id: endpointId, adapter: origin.platform },
|
|
73
|
+
kind: origin.scope,
|
|
74
|
+
id: origin.sceneId,
|
|
75
|
+
...(origin.threadId !== undefined ? { threadId: origin.threadId } : {}),
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Builds a LedgerCaller from an Agent Tool execution context. */
|
|
81
|
+
export function ledgerCallerFromTool(context: LedgerToolCallerContext): LedgerCaller {
|
|
82
|
+
const locale = resolveLocale(context.locale);
|
|
83
|
+
const senderId = (context.principal?.subjectId ?? context.senderId)?.trim();
|
|
84
|
+
if (!senderId) {
|
|
85
|
+
throw new LedgerInputError(t(locale, 'actor'));
|
|
86
|
+
}
|
|
87
|
+
const conversation = resolveToolConversation(context, locale);
|
|
88
|
+
const endpoint = conversation.endpoint;
|
|
89
|
+
const isPrivate = conversation.kind === 'private';
|
|
90
|
+
const privateTarget: DeliveryTarget = isPrivate
|
|
91
|
+
? conversation
|
|
92
|
+
: Object.freeze({ endpoint, kind: 'private', id: senderId });
|
|
93
|
+
return {
|
|
94
|
+
ownerKey: ledgerOwnerKey(endpoint.adapter, endpoint.id, senderId),
|
|
95
|
+
senderId,
|
|
96
|
+
locale,
|
|
97
|
+
conversation,
|
|
98
|
+
privateTarget,
|
|
99
|
+
isPrivate,
|
|
100
|
+
};
|
|
101
|
+
}
|