@niq.run/lark-worker 0.0.0-stage → 0.1.1

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 CHANGED
@@ -1,3 +1,71 @@
1
- # Temporary Holding Version
1
+ # @niq.run/lark-worker
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A **Feishu / Lark WebSocket bridge** for [niq](https://github.com/niq-run/niq).
4
+ It listens for Feishu messages, forwards them to a bound **reason worker** as
5
+ `worker.input` events, and pushes the reason worker's reply back into the
6
+ Feishu chat. Chat → worker routing is persistent and reconfigurable at runtime
7
+ (per-chat → default → fallback).
8
+
9
+ Built on [`@niq.run/worker-sdk`](https://www.npmjs.com/package/@niq.run/worker-sdk).
10
+ It is an out-of-process extension worker: it connects to a niq bus over
11
+ HTTP + SSE and is meant to be launched by the niq project supervisor (or by you,
12
+ for a standalone bridge).
13
+
14
+ ## Install
15
+
16
+ ```sh
17
+ npm install -g @niq.run/lark-worker
18
+ ```
19
+
20
+ ## Run
21
+
22
+ The package ships a `niq-lark` bin (compiled app entry `dist/start.js`), so you
23
+ can start it directly — with `npx` (no install needed):
24
+
25
+ ```sh
26
+ # npx — most convenient for launching, e.g. from a niq project's worker config
27
+ npx @niq.run/lark-worker --reason-worker reason.sales
28
+
29
+ # same thing once installed globally
30
+ niq-lark --reason-worker reason.sales
31
+
32
+ # or run it via node from a checkout
33
+ node dist/start.js --reason-worker reason.sales
34
+ ```
35
+
36
+ Connection to the bus comes from the environment the supervisor injects
37
+ (`NIQ_BUS_URL`, `NIQ_WORKER_ID`, `NIQ_WORKER_CREDENTIAL`), so a niq project can
38
+ spawn it with `npx @niq.run/lark-worker` and nothing else. CLI flags override
39
+ the env.
40
+
41
+ ## Configuration
42
+
43
+ | Purpose | Env | Flag |
44
+ |---|---|---|
45
+ | Feishu app id | `LARK_APP_ID` | `--app-id` |
46
+ | Feishu app secret | `LARK_APP_SECRET` | `--app-secret` |
47
+ | Feishu/Lark domain | `LARK_DOMAIN` | `--domain` |
48
+ | Proactive message recipient | `LARK_DEFAULT_USER_ID` | `--default-user` |
49
+ | **Default reason worker** | `NIQ_REASON_WORKER` | `--reason-worker` |
50
+ | **Fallback reason worker** | `LARK_FALLBACK_REASON_WORKER` | `--fallback-reason-worker` |
51
+ | **Per-chat routing** | `LARK_REASON_WORKER_MAPPINGS` | `--reason-mappings` |
52
+ | Persisted routing state | `LARK_STATE_FILE` | `--state-file` |
53
+
54
+ At least one of the default or fallback reason worker must be configured.
55
+ Run `niq-lark --help` for the full flag list.
56
+
57
+ ## Usage in code
58
+
59
+ ```ts
60
+ import { LarkWorker, larkConfigFromEnv } from "@niq.run/lark-worker";
61
+ ```
62
+
63
+ ## Details
64
+
65
+ See [`src/README.md`](src/README.md) for the full design: the `worker.input`
66
+ flow, `lark.reason.*` runtime routing (`lark.reason.set` / `unset` / `get`),
67
+ and the persisted routing state (`LARK_STATE_FILE`).
68
+
69
+ ## License
70
+
71
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=start.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":""}
package/dist/start.js ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Shipped as the binary `niq-lark` (@niq.run/lark-worker). Entry point for
4
+ * running the Lark/Feishu worker as its own process (the "worker as an app"
5
+ * model):
6
+ *
7
+ * node dist/start.js [--key value ...]
8
+ *
9
+ * Feishu long-connection + bus bridge to reason workers. Connects to Feishu
10
+ * (LARK_APP_ID / LARK_APP_SECRET) and the bus (NIQ_BUS_URL /
11
+ * NIQ_WORKER_ID / NIQ_WORKER_CREDENTIAL, merged by the SDK helper
12
+ * `busConnFromArgs`); forwards Feishu messages as worker.input to the default
13
+ * worker named by NIQ_REASON_WORKER (overridable per-chat via --reason-mappings
14
+ * / LARK_REASON_WORKER_MAPPINGS, a JSON object of {chat_id: worker_id}). When
15
+ * neither a per-chat mapping nor the default resolves, messages go to the
16
+ * last-resort fallback named by --fallback-reason-worker /
17
+ * LARK_FALLBACK_REASON_WORKER. The chat→worker routing is persisted
18
+ * (--state-file / LARK_STATE_FILE, default ./lark-reason-state.json).
19
+ *
20
+ * Proactive reason→lark messages use lark.send with no target and fall back to
21
+ * --default-user / LARK_DEFAULT_USER_ID (a Feishu open_id) when the reason
22
+ * worker supplies no explicit target.
23
+ */
24
+ import { HTTPWorkerClient, busConnFromArgs, parseCliArgs, runWorkerApp, } from "@niq.run/worker-sdk";
25
+ import { LarkWorker, larkConfigFromEnv } from "./worker.js";
26
+ function usage() {
27
+ console.error("usage: niq-lark [--key value ...]\n" +
28
+ " --bus-url <url> bus base URL (else NIQ_BUS_URL)\n" +
29
+ " --worker-id <id> bus identity (else NIQ_WORKER_ID)\n" +
30
+ " --credential <cred> bus credential (else NIQ_WORKER_CREDENTIAL)\n" +
31
+ " lark: --app-id <id>, --app-secret <secret> (default: LARK_APP_ID / LARK_APP_SECRET), " +
32
+ "--default-user <open_id> (default: LARK_DEFAULT_USER_ID), --reason-worker <id> (default: NIQ_REASON_WORKER), " +
33
+ "--fallback-reason-worker <id> (default: LARK_FALLBACK_REASON_WORKER), " +
34
+ "--reason-mappings <json> (default: LARK_REASON_WORKER_MAPPINGS; {chat_id: worker_id}), " +
35
+ "--state-file <path> (default: LARK_STATE_FILE / ./lark-reason-state.json)");
36
+ }
37
+ const { help, opts } = parseCliArgs(process.argv);
38
+ if (help) {
39
+ usage();
40
+ process.exit(0);
41
+ }
42
+ await runWorkerApp(async () => {
43
+ const bus = new HTTPWorkerClient(busConnFromArgs(opts));
44
+ const reasonWorkerID = String(opts["reason-worker"] ?? process.env.NIQ_REASON_WORKER ?? process.env.LARK_REASON_WORKER ?? "");
45
+ const fallbackReasonWorkerID = String(opts["fallback-reason-worker"] ?? process.env.LARK_FALLBACK_REASON_WORKER ?? "");
46
+ if (!reasonWorkerID && !fallbackReasonWorkerID) {
47
+ throw new Error("at least one of NIQ_REASON_WORKER / --reason-worker or LARK_FALLBACK_REASON_WORKER / --fallback-reason-worker must be set (bound reason worker id; fallback is the last resort)");
48
+ }
49
+ const lark = larkConfigFromEnv();
50
+ if (opts["app-id"] !== undefined)
51
+ lark.appId = String(opts["app-id"]);
52
+ if (opts["app-secret"] !== undefined)
53
+ lark.appSecret = String(opts["app-secret"]);
54
+ if (opts["domain"] !== undefined)
55
+ lark.domain = String(opts["domain"]);
56
+ const defaultUserOpenId = String(opts["default-user"] ?? process.env.LARK_DEFAULT_USER_ID ?? "");
57
+ // Optional initial chat → reason worker routing from config.
58
+ let reasonWorkerMappings;
59
+ const mappingsRaw = opts["reason-mappings"] ?? process.env.LARK_REASON_WORKER_MAPPINGS;
60
+ if (mappingsRaw) {
61
+ let parsed;
62
+ try {
63
+ parsed = JSON.parse(String(mappingsRaw));
64
+ }
65
+ catch (err) {
66
+ throw new Error(`invalid reason mappings JSON: ${err.message}`);
67
+ }
68
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
69
+ throw new Error("reason mappings must be an object of {chat_id: worker_id}");
70
+ }
71
+ reasonWorkerMappings = parsed;
72
+ }
73
+ const stateFile = String(opts["state-file"] ?? process.env.LARK_STATE_FILE ?? "");
74
+ const workerOpts = {
75
+ ...lark,
76
+ bus,
77
+ ...(reasonWorkerID ? { reasonWorkerID } : {}),
78
+ ...(fallbackReasonWorkerID ? { fallbackReasonWorkerID } : {}),
79
+ ...(reasonWorkerMappings ? { reasonWorkerMappings } : {}),
80
+ ...(stateFile ? { stateFile } : {}),
81
+ ...(defaultUserOpenId ? { defaultUserOpenId } : {}),
82
+ };
83
+ const worker = new LarkWorker(workerOpts);
84
+ await worker.run();
85
+ });
86
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,221 @@
1
+ import type { LarkChannelOptions, NormalizedMessage, RejectEvent, SendInput } from "@larksuite/channel";
2
+ import { type WorkerSideChannel } from "@niq.run/worker-sdk";
3
+ /** Minimal surface of `@larksuite/channel` this worker uses. Injectable for tests. */
4
+ export interface LarkChannel {
5
+ connect(): Promise<void>;
6
+ disconnect(): Promise<void>;
7
+ on(name: "message", handler: (msg: NormalizedMessage) => void | Promise<void>): () => void;
8
+ on(name: "error", handler: (err: unknown) => void | Promise<void>): () => void;
9
+ on(name: "reject", handler: (evt: RejectEvent) => void | Promise<void>): () => void;
10
+ on(name: "reconnecting" | "reconnected", handler: () => void | Promise<void>): () => void;
11
+ onRawEvent(type: string, handler: (payload: unknown) => void | Promise<void>): () => void;
12
+ send(to: string, input: SendInput): Promise<{
13
+ messageId: string;
14
+ }>;
15
+ /** Rewrite an already-sent text message's content in place (Feishu message edit). */
16
+ editMessage(messageId: string, text: string): Promise<void>;
17
+ }
18
+ /** A minimal logger interface, satisfied by `console`. */
19
+ export interface Logger {
20
+ info(...args: unknown[]): void;
21
+ warn(...args: unknown[]): void;
22
+ error(...args: unknown[]): void;
23
+ }
24
+ /** Wrap a logger so every line is prefixed with an ISO timestamp. */
25
+ export declare function withTimestamps<T extends Logger>(base: T): T;
26
+ /** The subset of the `fs/promises` API the file state store uses. Injectable. */
27
+ export interface StateFileFs {
28
+ mkdir(dir: string, opts: {
29
+ recursive: boolean;
30
+ }): Promise<string | undefined>;
31
+ readFile(file: string, enc: "utf8"): Promise<string>;
32
+ writeFile(file: string, data: string, enc: "utf8"): Promise<void>;
33
+ rename(oldPath: string, newPath: string): Promise<void>;
34
+ }
35
+ /**
36
+ * Persistence for the reason-routing state. Injected for tests; the default
37
+ * is {@link FileLarkStateStore}. This is the seam a future snapshot/restore
38
+ * implementation can plug into without touching the worker's logic.
39
+ */
40
+ export interface LarkStateStore {
41
+ load(): Promise<Record<string, unknown>>;
42
+ save(state: Record<string, unknown>): Promise<void>;
43
+ }
44
+ /**
45
+ * A {@link LarkStateStore} backed by a local JSON file, written atomically
46
+ * (temp file + rename) so a crash mid-write cannot corrupt the saved state.
47
+ * A missing file loads as an empty state.
48
+ */
49
+ export declare class FileLarkStateStore implements LarkStateStore {
50
+ private readonly file;
51
+ private readonly fs;
52
+ constructor(file: string, fsImpl?: StateFileFs);
53
+ load(): Promise<Record<string, unknown>>;
54
+ save(state: Record<string, unknown>): Promise<void>;
55
+ }
56
+ /** The default state file path when none is configured. */
57
+ export declare function defaultStateFile(env?: Record<string, string | undefined>): string;
58
+ /** Connection settings for the Lark side, read from the environment by default. */
59
+ export interface LarkConfig {
60
+ appId: string;
61
+ appSecret: string;
62
+ /** Request domain, e.g. `https://open.feishu.cn` (default) or a Lark domain. */
63
+ domain?: string;
64
+ /** Extra options forwarded to the channel (see {@link LarkChannelOptions}). */
65
+ channel?: Omit<LarkChannelOptions, "appId" | "appSecret" | "domain">;
66
+ /** Injectable factory — defaults to {@link createLarkChannel}. */
67
+ createChannel?: (opts: LarkChannelOptions) => LarkChannel;
68
+ }
69
+ /** Build a {@link LarkConfig} from env. Throws naming any missing vars. */
70
+ export declare function larkConfigFromEnv(env?: Record<string, string | undefined>): LarkConfig;
71
+ /** Configuration for the assembled bridge. */
72
+ export interface LarkBridgeOptions extends LarkConfig {
73
+ /** Worker-side bus channel. Injected for tests; built from env otherwise. */
74
+ bus: WorkerSideChannel;
75
+ /**
76
+ * The default reason worker Feishu messages are forwarded to when a chat has
77
+ * no per-chat override (see {@link LarkBridgeOptions.reasonWorkerMappings}),
78
+ * unless a persisted state file overrides it. Optional: when unset, routing
79
+ * falls through to {@link LarkBridgeOptions.fallbackReasonWorkerID}. Config
80
+ * only seeds this value.
81
+ */
82
+ reasonWorkerID?: string;
83
+ /**
84
+ * The last-resort reason worker used when neither a per-chat mapping nor the
85
+ * default resolves. Optional; config only seeds it (mutable at runtime and
86
+ * persisted like the rest of the routing).
87
+ */
88
+ fallbackReasonWorkerID?: string;
89
+ /**
90
+ * Initial chat_id → reason worker id routing. Config only seeds the runtime
91
+ * map; it is updateable at runtime via the `lark.reason.*` extensions and
92
+ * persisted (see {@link LarkBridgeOptions.stateStore}).
93
+ */
94
+ reasonWorkerMappings?: Record<string, string>;
95
+ /**
96
+ * Persistence backend for the reason-routing state. Defaults to a local JSON
97
+ * file at {@link LarkBridgeOptions.stateFile}. Inject for tests to avoid I/O.
98
+ * This is the seam a future snapshot/restore implementation plugs into.
99
+ */
100
+ stateStore?: LarkStateStore;
101
+ /**
102
+ * Path for the default file state store (ignored when `stateStore` is given).
103
+ * Falls back to `LARK_STATE_FILE`, then `./lark-reason-state.json`.
104
+ */
105
+ stateFile?: string;
106
+ /**
107
+ * Optional replacement for the default `<system-reminder>` prepended to
108
+ * outbound worker.input text.
109
+ */
110
+ systemReminder?: string;
111
+ /** When set, echo Feishu messages back instead of forwarding (connectivity check). */
112
+ echo?: boolean;
113
+ /**
114
+ * Default Feishu user (open_id) a `lark.send` call falls back to when the
115
+ * reason worker does not supply an explicit `target` (e.g. a proactive push
116
+ * with no chat context). Without a target and no default user, the call
117
+ * fails with `request.failed`.
118
+ */
119
+ defaultUserOpenId?: string;
120
+ /**
121
+ * When a reason worker makes several `lark.send` calls to the same target
122
+ * within one reply turn, append each piece of text onto the message it last
123
+ * sent to that target (updating it in place via Feishu's message edit) instead
124
+ * of posting a brand-new message per call. Each new inbound Feishu message
125
+ * starts a fresh target message. Defaults to `true`.
126
+ */
127
+ appendOutbound?: boolean;
128
+ /** Where to send diagnostics. Defaults to `console`. */
129
+ logger?: Logger;
130
+ }
131
+ /**
132
+ * The extension lark exposes to peers (reason) for sending a Feishu message to
133
+ * a user/chat. Non-SelfOnly so it is discovered by reason and callable.
134
+ */
135
+ export declare const LARK_SEND_EVENT = "lark.send";
136
+ /**
137
+ * The group of extensions lark exposes for managing the chat → reason worker
138
+ * routing at runtime. All are non-SelfOnly so a control plane / admin peer can
139
+ * discover and call them. Each mutation also persists the new routing.
140
+ */
141
+ /** Route a Feishu chat (or set the default) to a specific reason worker. */
142
+ export declare const LARK_REASON_SET = "lark.reason.set";
143
+ /** Stop routing a chat to its own worker; fall back to the default. */
144
+ export declare const LARK_REASON_UNSET = "lark.reason.unset";
145
+ /** Return the current routing (default + per-chat overrides). */
146
+ export declare const LARK_REASON_GET = "lark.reason.get";
147
+ /**
148
+ * The Feishu ⇄ reason bridge. Connect both sides, then forward Feishu
149
+ * messages to the bound reason worker and push its replies back to Feishu.
150
+ */
151
+ export declare class LarkWorker {
152
+ private readonly lark;
153
+ private readonly bus;
154
+ private readonly base;
155
+ private readonly workerID;
156
+ /** Default reason worker; overridden per-chat by {@link perChat}. Mutable at runtime. */
157
+ private defaultReasonWorker;
158
+ /** Last-resort worker used when neither per-chat nor default resolves. Mutable at runtime. */
159
+ private fallbackReasonWorker;
160
+ /** chat_id → reason worker id. Updated by config seed, runtime events, and persisted state. */
161
+ private readonly perChat;
162
+ private readonly stateStore;
163
+ private readonly systemReminder;
164
+ private readonly echo;
165
+ private readonly defaultUserOpenId;
166
+ private readonly appendMode;
167
+ /** target → the last outbound message this worker is extending (append mode); text is the in-place accumulated content. */
168
+ private readonly appendTargets;
169
+ private readonly logger;
170
+ /** Settled once both the bus and Feishu sides are connected (see connect()). */
171
+ private readonly ready;
172
+ private readonly markReady;
173
+ constructor(opts: LarkBridgeOptions);
174
+ /** Connect both the Feishu long connection and the bus; announce presence. */
175
+ connect(): Promise<void>;
176
+ /** Connect and then run the bus event loop (blocks until {@link close}). */
177
+ run(): Promise<void>;
178
+ /** Close both sides. */
179
+ close(): Promise<void>;
180
+ private handleFeishuMessage;
181
+ /**
182
+ * Resolve the reason worker a chat's messages are forwarded to: per-chat
183
+ * mapping, else the default, else the last-resort fallback. Returns "" when
184
+ * none is configured.
185
+ */
186
+ private reasonWorkerFor;
187
+ /**
188
+ * Restore persisted routing on top of the config seed. A persisted value wins
189
+ * over config because it is the most recent state (last write wins).
190
+ */
191
+ private loadState;
192
+ /** Persist the current routing. Best-effort: a failed write is logged, not thrown. */
193
+ private persistState;
194
+ /** Fill the {chat_id} / {sender_open_id} placeholders in the reminder template. */
195
+ private renderReminder;
196
+ /**
197
+ * Serve the peer-callable lark.send extension: a tool invocation from reason
198
+ * (via the discovery/tool bridge) carrying `target` (chat_id / open_id /
199
+ * user_id, falling back to the default user) and `text`. Replied with a
200
+ * request.completed / request.failed echoing the call id.
201
+ */
202
+ private handleLarkSend;
203
+ /**
204
+ * Append-mode send: if this target already has an in-progress message from
205
+ * this turn, extend it in place; otherwise post a fresh message and remember
206
+ * it. Repeated `lark.send` calls to the same target therefore assemble into
207
+ * one message instead of flooding the Feishu chat.
208
+ */
209
+ private sendAppended;
210
+ /**
211
+ * Serve the `lark.reason.set` extension: route a chat to a worker (`chat_id`),
212
+ * set the default (neither `chat_id` nor `fallback`), or set the last-resort
213
+ * fallback (`fallback: true`). Persists the change so it survives a restart.
214
+ */
215
+ private handleReasonSet;
216
+ /** Serve the `lark.reason.unset` extension: drop a chat override or clear the fallback. */
217
+ private handleReasonUnset;
218
+ /** Serve the `lark.reason.get` extension: return the current routing as JSON. */
219
+ private handleReasonGet;
220
+ }
221
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
package/dist/worker.js ADDED
@@ -0,0 +1,552 @@
1
+ /**
2
+ * Lark worker — a WebSocket bridge between the niq event bus and Feishu/Lark,
3
+ * built on `@larksuite/channel` (which wraps the official
4
+ * `@larksuiteoapi/node-sdk`).
5
+ *
6
+ * Flow:
7
+ * Feishu message ──worker.input──▶ bound reason worker
8
+ * reason reply ──lark.send────▶ this worker ──send──▶ Feishu chat
9
+ *
10
+ * Inbound: every Feishu message is forwarded to a single bound reason worker
11
+ * as a `worker.input` event with `input_mode: "append"`, payload
12
+ * `{ text, chat_id, sender_open_id }`. The text is prefixed with a
13
+ * `<system-reminder>` telling reason this came from Feishu and to reply with
14
+ * the `lark.send` extension targeting the `chat_id`.
15
+ *
16
+ * Outbound: the reason worker replies by calling the `lark.send` extension
17
+ * (exposed by this worker) with `target` (a chat_id / open_id / user_id) and
18
+ * `text`. This worker sends that text to the target Feishu chat. The worker
19
+ * does NOT consume `worker.input` events as replies — `lark.send` is the only
20
+ * inbound path for reason → Feishu output.
21
+ *
22
+ * Credentials come from the environment: `LARK_APP_ID` / `LARK_APP_SECRET`
23
+ * (+ `LARK_DOMAIN`), and the bus via `NIQ_BUS_URL` / `NIQ_WORKER_ID` /
24
+ * `NIQ_WORKER_CREDENTIAL` — the niq project supervisor injects these as launch
25
+ * args (project.json env). The default reason worker id comes from
26
+ * `NIQ_REASON_WORKER`; an initial per-chat override map can come from
27
+ * `LARK_REASON_WORKER_MAPPINGS` (a JSON object of {chat_id: worker_id}).
28
+ *
29
+ * Routing (which reason worker a Feishu chat is forwarded to) is a mutable,
30
+ * persisted property: configured on startup, updated at runtime via the
31
+ * `lark.reason.*` extension group, and saved to a state file so it survives
32
+ * restarts. The runtime saves it via an injectable {@link LarkStateStore}.
33
+ * (Third-party process snapshot/restore is not implemented yet; the worker
34
+ * persists its own state for now.)
35
+ *
36
+ * Both external sides sit behind narrow interfaces so tests run without real
37
+ * Feishu credentials or a live bus.
38
+ */
39
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
40
+ import { dirname } from "node:path";
41
+ import { createLarkChannel } from "@larksuite/channel";
42
+ import { BaseWorker, argString, createEvent, EventType, parseToolCall, } from "@niq.run/worker-sdk";
43
+ /** Wrap a logger so every line is prefixed with an ISO timestamp. */
44
+ export function withTimestamps(base) {
45
+ const ts = () => new Date().toISOString();
46
+ for (const m of ["info", "warn", "error"]) {
47
+ const orig = base[m].bind(base);
48
+ base[m] = (...args) => orig(`[${ts()}]`, ...args);
49
+ }
50
+ return base;
51
+ }
52
+ const DEFAULT_LOGGER = withTimestamps(console);
53
+ /**
54
+ * A {@link LarkStateStore} backed by a local JSON file, written atomically
55
+ * (temp file + rename) so a crash mid-write cannot corrupt the saved state.
56
+ * A missing file loads as an empty state.
57
+ */
58
+ export class FileLarkStateStore {
59
+ file;
60
+ fs;
61
+ constructor(file, fsImpl = { mkdir, readFile, writeFile, rename }) {
62
+ this.file = file;
63
+ this.fs = fsImpl;
64
+ }
65
+ async load() {
66
+ let raw;
67
+ try {
68
+ raw = await this.fs.readFile(this.file, "utf8");
69
+ }
70
+ catch (err) {
71
+ // First run: no state file yet — start from config defaults.
72
+ if (err?.code === "ENOENT")
73
+ return {};
74
+ throw err;
75
+ }
76
+ const parsed = JSON.parse(raw);
77
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
78
+ ? parsed
79
+ : {};
80
+ }
81
+ async save(state) {
82
+ const tmp = `${this.file}.${process.pid}.${Date.now()}.tmp`;
83
+ await this.fs.mkdir(dirname(this.file), { recursive: true });
84
+ await this.fs.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
85
+ await this.fs.rename(tmp, this.file);
86
+ }
87
+ }
88
+ /** The default state file path when none is configured. */
89
+ export function defaultStateFile(env = process.env) {
90
+ return env.LARK_STATE_FILE?.trim() || "lark-reason-state.json";
91
+ }
92
+ /** Build a {@link LarkConfig} from env. Throws naming any missing vars. */
93
+ export function larkConfigFromEnv(env = process.env) {
94
+ const appId = env.LARK_APP_ID?.trim() ?? "";
95
+ const appSecret = env.LARK_APP_SECRET?.trim() ?? "";
96
+ const domain = env.LARK_DOMAIN?.trim() || "https://open.feishu.cn";
97
+ const missing = [];
98
+ if (!appId)
99
+ missing.push("LARK_APP_ID");
100
+ if (!appSecret)
101
+ missing.push("LARK_APP_SECRET");
102
+ if (missing.length > 0) {
103
+ throw new Error(`niq: missing environment variable(s): ${missing.join(", ")}. ` +
104
+ `Provide them as launch args (project.json env) when the worker is ` +
105
+ `started by the niq project supervisor.`);
106
+ }
107
+ return { appId, appSecret, domain };
108
+ }
109
+ const DEFAULT_SYSTEM_REMINDER = `<system-reminder>
110
+ This message was sent by a user over Feishu (Lark).
111
+ Chat id: {chat_id}
112
+ Sender open_id: {sender_open_id}
113
+ Reply to the user by calling the lark.send tool with target = "{chat_id}" and
114
+ text = your reply. The reply is delivered straight back into the user's Feishu
115
+ chat. Several lark.send calls to the same target within one reply are appended
116
+ into a single message, so just send your answer — do not number or header each
117
+ piece. Keep your answer in the language the user wrote in, and do not preface it
118
+ with any system notes.
119
+ </system-reminder>`;
120
+ const NS = "lark";
121
+ /**
122
+ * The extension lark exposes to peers (reason) for sending a Feishu message to
123
+ * a user/chat. Non-SelfOnly so it is discovered by reason and callable.
124
+ */
125
+ export const LARK_SEND_EVENT = "lark.send";
126
+ /**
127
+ * The group of extensions lark exposes for managing the chat → reason worker
128
+ * routing at runtime. All are non-SelfOnly so a control plane / admin peer can
129
+ * discover and call them. Each mutation also persists the new routing.
130
+ */
131
+ /** Route a Feishu chat (or set the default) to a specific reason worker. */
132
+ export const LARK_REASON_SET = "lark.reason.set";
133
+ /** Stop routing a chat to its own worker; fall back to the default. */
134
+ export const LARK_REASON_UNSET = "lark.reason.unset";
135
+ /** Return the current routing (default + per-chat overrides). */
136
+ export const LARK_REASON_GET = "lark.reason.get";
137
+ /**
138
+ * The Feishu ⇄ reason bridge. Connect both sides, then forward Feishu
139
+ * messages to the bound reason worker and push its replies back to Feishu.
140
+ */
141
+ export class LarkWorker {
142
+ lark;
143
+ bus;
144
+ base;
145
+ workerID;
146
+ /** Default reason worker; overridden per-chat by {@link perChat}. Mutable at runtime. */
147
+ defaultReasonWorker;
148
+ /** Last-resort worker used when neither per-chat nor default resolves. Mutable at runtime. */
149
+ fallbackReasonWorker;
150
+ /** chat_id → reason worker id. Updated by config seed, runtime events, and persisted state. */
151
+ perChat = new Map();
152
+ stateStore;
153
+ systemReminder;
154
+ echo;
155
+ defaultUserOpenId;
156
+ appendMode;
157
+ /** target → the last outbound message this worker is extending (append mode); text is the in-place accumulated content. */
158
+ appendTargets = new Map();
159
+ logger;
160
+ /** Settled once both the bus and Feishu sides are connected (see connect()). */
161
+ ready;
162
+ markReady;
163
+ constructor(opts) {
164
+ const create = opts.createChannel ?? createLarkChannel;
165
+ this.lark = create({
166
+ appId: opts.appId,
167
+ appSecret: opts.appSecret,
168
+ domain: opts.domain,
169
+ ...opts.channel,
170
+ });
171
+ this.bus = opts.bus;
172
+ this.workerID = opts.bus.id;
173
+ // Config seeds the routing state; a persisted state file overrides it on load.
174
+ this.defaultReasonWorker = opts.reasonWorkerID ?? "";
175
+ this.fallbackReasonWorker = opts.fallbackReasonWorkerID ?? "";
176
+ for (const [chat, wid] of Object.entries(opts.reasonWorkerMappings ?? {})) {
177
+ if (wid)
178
+ this.perChat.set(chat, wid);
179
+ }
180
+ this.stateStore =
181
+ opts.stateStore ??
182
+ new FileLarkStateStore(opts.stateFile ?? defaultStateFile());
183
+ this.systemReminder = opts.systemReminder ?? DEFAULT_SYSTEM_REMINDER;
184
+ this.echo = opts.echo ?? false;
185
+ this.defaultUserOpenId = opts.defaultUserOpenId ?? "";
186
+ this.appendMode = opts.appendOutbound ?? true;
187
+ this.logger = opts.logger ?? DEFAULT_LOGGER;
188
+ this.base = new BaseWorker({
189
+ id: this.workerID,
190
+ subscriptions: [
191
+ { type: LARK_REASON_SET },
192
+ { type: LARK_REASON_UNSET },
193
+ { type: LARK_REASON_GET },
194
+ ],
195
+ channel: this.bus,
196
+ });
197
+ // Expose a peer-callable extension so reason can send Feishu messages with
198
+ // an explicit target (chat_id / open_id / user_id). Non-SelfOnly so it is
199
+ // discovered by reason and invoked via the tool-name bridge.
200
+ this.base.register({
201
+ event: LARK_SEND_EVENT,
202
+ description: "Send a message to a Feishu user or chat. Provide target (chat_id, open_id, or user_id) and text.",
203
+ parameters: {
204
+ type: "object",
205
+ properties: {
206
+ target: {
207
+ type: "string",
208
+ description: "Feishu chat_id, open_id, or user_id of the recipient",
209
+ },
210
+ text: { type: "string", description: "Message text" },
211
+ },
212
+ },
213
+ }, (evt) => this.handleLarkSend(evt));
214
+ // The lark.reason.* group: manage the chat → reason worker routing at
215
+ // runtime. Every mutation is persisted so the routing survives restarts.
216
+ this.base.register({
217
+ event: LARK_REASON_SET,
218
+ description: "Route a Feishu chat to a reason worker, set the default, or set the fallback. " +
219
+ "Provide worker_id. Include chat_id to route only that chat; include fallback=true " +
220
+ "(no chat_id) to set the last-resort fallback; otherwise the default reason worker is set.",
221
+ parameters: {
222
+ type: "object",
223
+ properties: {
224
+ chat_id: {
225
+ type: "string",
226
+ description: "Feishu chat_id to route (omit to set the default or fallback)",
227
+ },
228
+ worker_id: {
229
+ type: "string",
230
+ description: "Reason worker id to route matching messages to",
231
+ },
232
+ fallback: {
233
+ type: "boolean",
234
+ description: "Set the last-resort fallback reason worker instead of the default (ignored when chat_id is given)",
235
+ },
236
+ },
237
+ },
238
+ }, (evt) => this.handleReasonSet(evt));
239
+ this.base.register({
240
+ event: LARK_REASON_UNSET,
241
+ description: "Stop routing a Feishu chat to its own reason worker and fall back to the default, " +
242
+ "or clear the fallback. Provide chat_id to drop a chat override; include fallback=true " +
243
+ "(chat_id optional) to clear the fallback.",
244
+ parameters: {
245
+ type: "object",
246
+ properties: {
247
+ chat_id: {
248
+ type: "string",
249
+ description: "Feishu chat_id to stop overriding, falling back to the default",
250
+ },
251
+ fallback: {
252
+ type: "boolean",
253
+ description: "Clear the fallback reason worker",
254
+ },
255
+ },
256
+ },
257
+ }, (evt) => this.handleReasonUnset(evt));
258
+ this.base.register({
259
+ event: LARK_REASON_GET,
260
+ description: "Return the current chat → reason worker routing (default + per-chat overrides).",
261
+ }, (evt) => this.handleReasonGet(evt));
262
+ let markReady;
263
+ this.ready = new Promise((resolve) => {
264
+ markReady = resolve;
265
+ });
266
+ this.markReady = markReady;
267
+ // The message handler is registered once, in the constructor, so a Feishu
268
+ // message can never be missed due to connect() ordering. handleFeishuMessage
269
+ // awaits this.ready before touching the bus, which preserves the original
270
+ // guarantee: we never publish to the bus before it is connected (the WS can
271
+ // be live before the bus SSE is ready).
272
+ this.lark.on("message", (msg) => this.handleFeishuMessage(msg));
273
+ this.lark.on("error", (err) => this.logger.error(`[${NS}] feishu error:`, err));
274
+ this.lark.on("reject", (evt) => this.logger.warn(`[${NS}] feishu message REJECTED by policy: ${JSON.stringify(evt)}`));
275
+ // Lowest-level probe: logs any im.message.receive_v1 the WS actually
276
+ // delivers, before normalization / policy. If this fires but the
277
+ // normalized 'message' event does not, the drop is in the channel's
278
+ // normalization or policy layer, not in Feishu delivery.
279
+ this.lark.onRawEvent("im.message.receive_v1", () => this.logger.info(`[${NS}] raw im.message.receive_v1 received on WS`));
280
+ this.lark.on("reconnecting", () => this.logger.warn(`[${NS}] feishu websocket reconnecting`));
281
+ this.lark.on("reconnected", () => this.logger.info(`[${NS}] feishu websocket reconnected`));
282
+ }
283
+ /** Connect both the Feishu long connection and the bus; announce presence. */
284
+ async connect() {
285
+ await Promise.all([
286
+ this.bus.connect().catch((err) => {
287
+ this.logger.error(`[${NS}] bus connect failed:`, err);
288
+ throw err;
289
+ }),
290
+ this.lark.connect().catch((err) => {
291
+ this.logger.error(`[${NS}] feishu connect failed:`, err);
292
+ throw err;
293
+ }),
294
+ ]);
295
+ // Restore persisted routing before being ready, so the first Feishu message
296
+ // already uses the saved chat → reason worker mapping.
297
+ await this.loadState();
298
+ this.markReady();
299
+ this.logger.info(`[${NS}] connected: bus worker_id=${this.workerID} default_reason=${this.defaultReasonWorker || "—"} fallback=${this.fallbackReasonWorker || "—"} per_chat=${this.perChat.size} feishu app=${this.lark.constructor.name}`);
300
+ // Presence is live-only, not durable history. The SDK's announceReady would
301
+ // persist this worker.ready into the project event store (it sets no
302
+ // transient flag); a presence stalling the durable log is noise. Broadcast
303
+ // a transient ready instead so lark's presence never lands in events.db.
304
+ const ready = createEvent(EventType.WorkerReady, {
305
+ type: "lark",
306
+ watch: this.base.watchEntries(false),
307
+ publishes: [
308
+ { type: "worker.input", description: "Feishu messages forwarded to reason" },
309
+ ],
310
+ });
311
+ ready.transient = true; // presence: live-only, not durable history
312
+ ready.exclude_worker_id = this.workerID;
313
+ await this.base.channel.broadcast(ready);
314
+ this.logger.info(`[${NS}] announced worker.ready (transient)`);
315
+ }
316
+ /** Connect and then run the bus event loop (blocks until {@link close}). */
317
+ async run() {
318
+ await this.connect();
319
+ // The worker never acts on `worker.input` events — reason does not reply
320
+ // to lark that way. Replies arrive only via the `lark.send` extension, and
321
+ // routing is managed via the `lark.reason.*` extensions; every event is
322
+ // dispatched to the registered extension handlers (unknown events are
323
+ // ignored).
324
+ for await (const evt of this.bus.events()) {
325
+ this.base.dispatchExtension(evt);
326
+ }
327
+ }
328
+ /** Close both sides. */
329
+ async close() {
330
+ await Promise.all([
331
+ this.bus.close().catch(() => { }),
332
+ this.lark.disconnect().catch(() => { }),
333
+ ]);
334
+ }
335
+ async handleFeishuMessage(msg) {
336
+ // Wait until the bus is connected before doing anything, so we never
337
+ // publish before the bus is up. The handler is registered in the
338
+ // constructor, so a message arriving during connect() simply waits here.
339
+ await this.ready;
340
+ // Don't relay the bot's own outbound messages if Feishu delivers them.
341
+ if (msg.senderType === "bot") {
342
+ this.logger.info(`[${NS}] skip bot-originated message chat=${msg.chatId} msg=${msg.messageId}`);
343
+ return;
344
+ }
345
+ this.logger.info(`[${NS}] feishu message chat=${msg.chatId} msg=${msg.messageId} sender=${msg.senderId} type=${msg.senderType ?? "na"} text=${JSON.stringify(msg.content)}`);
346
+ if (this.echo) {
347
+ await this.lark.send(msg.chatId, { text: "echo: " + msg.content });
348
+ return;
349
+ }
350
+ // A new user message begins a fresh reply: forget any in-progress
351
+ // outbound append so the next lark.send posts a new message instead of
352
+ // extending the previous turn's.
353
+ this.appendTargets.delete(msg.chatId);
354
+ const input = createEvent("worker.input", {
355
+ text: this.renderReminder(msg) + "\n" + msg.content,
356
+ chat_id: msg.chatId,
357
+ sender_open_id: msg.senderId,
358
+ input_mode: "append",
359
+ });
360
+ const reasonWorker = this.reasonWorkerFor(msg.chatId);
361
+ if (!reasonWorker) {
362
+ this.logger.warn(`[${NS}] drop feishu message chat=${msg.chatId} msg=${msg.messageId}: no reason worker (no per-chat mapping, default, or fallback)`);
363
+ return;
364
+ }
365
+ await this.bus.send(input, reasonWorker);
366
+ this.logger.info(`[${NS}] sent worker.input → reason ${reasonWorker} (chat ${msg.chatId})`);
367
+ }
368
+ /**
369
+ * Resolve the reason worker a chat's messages are forwarded to: per-chat
370
+ * mapping, else the default, else the last-resort fallback. Returns "" when
371
+ * none is configured.
372
+ */
373
+ reasonWorkerFor(chatId) {
374
+ const perChat = this.perChat.get(chatId);
375
+ if (perChat)
376
+ return perChat;
377
+ if (this.defaultReasonWorker)
378
+ return this.defaultReasonWorker;
379
+ return this.fallbackReasonWorker;
380
+ }
381
+ /**
382
+ * Restore persisted routing on top of the config seed. A persisted value wins
383
+ * over config because it is the most recent state (last write wins).
384
+ */
385
+ async loadState() {
386
+ let state;
387
+ try {
388
+ state = await this.stateStore.load();
389
+ }
390
+ catch (err) {
391
+ this.logger.error(`[${NS}] failed to load reason state:`, err);
392
+ return;
393
+ }
394
+ if (typeof state["default_reason_worker"] === "string" && state["default_reason_worker"]) {
395
+ this.defaultReasonWorker = state["default_reason_worker"];
396
+ }
397
+ if (typeof state["fallback_reason_worker"] === "string" && state["fallback_reason_worker"]) {
398
+ this.fallbackReasonWorker = state["fallback_reason_worker"];
399
+ }
400
+ const perChat = state["per_chat"];
401
+ if (perChat && typeof perChat === "object" && !Array.isArray(perChat)) {
402
+ this.perChat.clear();
403
+ for (const [chat, wid] of Object.entries(perChat)) {
404
+ if (typeof wid === "string" && wid)
405
+ this.perChat.set(chat, wid);
406
+ }
407
+ }
408
+ this.logger.info(`[${NS}] loaded reason state: default=${this.defaultReasonWorker || "—"} fallback=${this.fallbackReasonWorker || "—"} per_chat=${this.perChat.size}`);
409
+ }
410
+ /** Persist the current routing. Best-effort: a failed write is logged, not thrown. */
411
+ async persistState() {
412
+ try {
413
+ await this.stateStore.save({
414
+ default_reason_worker: this.defaultReasonWorker,
415
+ fallback_reason_worker: this.fallbackReasonWorker,
416
+ per_chat: Object.fromEntries(this.perChat),
417
+ });
418
+ }
419
+ catch (err) {
420
+ this.logger.error(`[${NS}] failed to persist reason state:`, err);
421
+ }
422
+ }
423
+ /** Fill the {chat_id} / {sender_open_id} placeholders in the reminder template. */
424
+ renderReminder(msg) {
425
+ return this.systemReminder
426
+ .replaceAll("{chat_id}", msg.chatId)
427
+ .replaceAll("{sender_open_id}", msg.senderId);
428
+ }
429
+ /**
430
+ * Serve the peer-callable lark.send extension: a tool invocation from reason
431
+ * (via the discovery/tool bridge) carrying `target` (chat_id / open_id /
432
+ * user_id, falling back to the default user) and `text`. Replied with a
433
+ * request.completed / request.failed echoing the call id.
434
+ */
435
+ async handleLarkSend(evt) {
436
+ const tc = parseToolCall(evt);
437
+ const target = argString(tc.args, "target") ||
438
+ argString(tc.args, "chat_id") ||
439
+ argString(tc.args, "open_id") ||
440
+ argString(tc.args, "user_id") ||
441
+ this.defaultUserOpenId;
442
+ const text = argString(tc.args, "text");
443
+ if (!text) {
444
+ await this.base.replyFailed(tc.callerID, tc.callID, "text is required", tc.traceID);
445
+ return;
446
+ }
447
+ if (!target) {
448
+ await this.base.replyFailed(tc.callerID, tc.callID, "no target given and no default user open_id configured", tc.traceID);
449
+ return;
450
+ }
451
+ try {
452
+ if (this.appendMode) {
453
+ await this.sendAppended(target, text);
454
+ }
455
+ else {
456
+ await this.lark.send(target, { text });
457
+ this.logger.info(`[${NS}] lark.send → ${target} text=${JSON.stringify(text)}`);
458
+ }
459
+ await this.base.replyCompleted(tc.callerID, tc.callID, "sent", tc.traceID);
460
+ }
461
+ catch (err) {
462
+ // Failed mid-append: drop the half-updated entry so the next send starts
463
+ // a fresh message rather than corrupting the accumulated text.
464
+ this.appendTargets.delete(target);
465
+ this.logger.error(`[${NS}] lark.send failed:`, err);
466
+ await this.base.replyFailed(tc.callerID, tc.callID, err.message, tc.traceID);
467
+ }
468
+ }
469
+ /**
470
+ * Append-mode send: if this target already has an in-progress message from
471
+ * this turn, extend it in place; otherwise post a fresh message and remember
472
+ * it. Repeated `lark.send` calls to the same target therefore assemble into
473
+ * one message instead of flooding the Feishu chat.
474
+ */
475
+ async sendAppended(target, text) {
476
+ const pending = this.appendTargets.get(target);
477
+ if (!pending) {
478
+ const res = await this.lark.send(target, { text });
479
+ this.appendTargets.set(target, { messageId: res.messageId, text });
480
+ this.logger.info(`[${NS}] lark.send → ${target} text=${JSON.stringify(text)}`);
481
+ return;
482
+ }
483
+ // Join segments with a single newline so separate sends read as distinct
484
+ // parts, not run-on text.
485
+ const merged = pending.text.replace(/\s+$/, "") + "\n" + text.replace(/\s+$/, "");
486
+ await this.lark.editMessage(pending.messageId, merged);
487
+ pending.text = merged;
488
+ this.logger.info(`[${NS}] lark.append → ${target} (extended ${pending.messageId})`);
489
+ }
490
+ /**
491
+ * Serve the `lark.reason.set` extension: route a chat to a worker (`chat_id`),
492
+ * set the default (neither `chat_id` nor `fallback`), or set the last-resort
493
+ * fallback (`fallback: true`). Persists the change so it survives a restart.
494
+ */
495
+ async handleReasonSet(evt) {
496
+ const tc = parseToolCall(evt);
497
+ const workerId = argString(tc.args, "worker_id") || argString(tc.args, "workerId");
498
+ const chatId = argString(tc.args, "chat_id") || argString(tc.args, "chatId");
499
+ const isFallback = tc.args["fallback"] === true;
500
+ if (!workerId) {
501
+ await this.base.replyFailed(tc.callerID, tc.callID, "worker_id is required", tc.traceID);
502
+ return;
503
+ }
504
+ if (chatId) {
505
+ this.perChat.set(chatId, workerId);
506
+ this.logger.info(`[${NS}] lark.reason.set chat=${chatId} → ${workerId}`);
507
+ }
508
+ else if (isFallback) {
509
+ this.fallbackReasonWorker = workerId;
510
+ this.logger.info(`[${NS}] lark.reason.set fallback → ${workerId}`);
511
+ }
512
+ else {
513
+ this.defaultReasonWorker = workerId;
514
+ this.logger.info(`[${NS}] lark.reason.set default → ${workerId}`);
515
+ }
516
+ await this.persistState();
517
+ await this.base.replyCompleted(tc.callerID, tc.callID, "ok", tc.traceID);
518
+ }
519
+ /** Serve the `lark.reason.unset` extension: drop a chat override or clear the fallback. */
520
+ async handleReasonUnset(evt) {
521
+ const tc = parseToolCall(evt);
522
+ const chatId = argString(tc.args, "chat_id") || argString(tc.args, "chatId");
523
+ const isFallback = tc.args["fallback"] === true;
524
+ if (isFallback) {
525
+ this.fallbackReasonWorker = "";
526
+ await this.persistState();
527
+ await this.base.replyCompleted(tc.callerID, tc.callID, "ok", tc.traceID);
528
+ this.logger.info(`[${NS}] lark.reason.unset fallback cleared`);
529
+ return;
530
+ }
531
+ if (!chatId) {
532
+ await this.base.replyFailed(tc.callerID, tc.callID, "chat_id is required (or set fallback=true to clear the fallback)", tc.traceID);
533
+ return;
534
+ }
535
+ this.perChat.delete(chatId);
536
+ await this.persistState();
537
+ await this.base.replyCompleted(tc.callerID, tc.callID, "ok", tc.traceID);
538
+ this.logger.info(`[${NS}] lark.reason.unset chat=${chatId} → default ${this.defaultReasonWorker}`);
539
+ }
540
+ /** Serve the `lark.reason.get` extension: return the current routing as JSON. */
541
+ async handleReasonGet(evt) {
542
+ const tc = parseToolCall(evt);
543
+ const result = JSON.stringify({
544
+ default_reason_worker: this.defaultReasonWorker,
545
+ fallback_reason_worker: this.fallbackReasonWorker,
546
+ per_chat: Object.fromEntries(this.perChat),
547
+ });
548
+ await this.base.replyCompleted(tc.callerID, tc.callID, result, tc.traceID);
549
+ this.logger.info(`[${NS}] lark.reason.get default=${this.defaultReasonWorker || "—"} fallback=${this.fallbackReasonWorker || "—"} per_chat=${this.perChat.size}`);
550
+ }
551
+ }
552
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "@niq.run/lark-worker",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1",
4
+ "description": "Feishu/Lark WebSocket bridge Worker for niq — forwards Feishu messages to a bound reason worker and pushes its replies back. Built on @niq.run/worker-sdk.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/niq-run/workers.git"
8
+ },
9
+ "homepage": "https://www.npmjs.com/package/@niq.run/lark-worker",
10
+ "type": "module",
11
+ "main": "./dist/worker.js",
12
+ "types": "./dist/worker.d.ts",
13
+ "bin": {
14
+ "niq-lark": "./dist/start.js"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "sideEffects": false,
23
+ "scripts": {
24
+ "build": "tsc -p tsconfig.build.json",
25
+ "test": "vitest run",
26
+ "typecheck": "tsc --noEmit",
27
+ "release": "git diff --quiet && git diff --cached --quiet || { echo \"Error: git working tree not clean\" >&2; exit 1; } && npm run test && npm run typecheck && npm run build && npm version --no-git-tag-version && npm publish"
28
+ },
29
+ "dependencies": {
30
+ "@larksuite/channel": "^0.6.0",
31
+ "@niq.run/worker-sdk": "*"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^24.13.3",
35
+ "typescript": "^5.8.3",
36
+ "vitest": "^3.2.0"
37
+ },
38
+ "license": "MIT"
6
39
  }