@oyasmi/pipiclaw 0.5.0 → 0.5.2
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/dist/agent/channel-runner.d.ts +46 -0
- package/dist/agent/channel-runner.d.ts.map +1 -0
- package/dist/agent/channel-runner.js +434 -0
- package/dist/agent/channel-runner.js.map +1 -0
- package/dist/agent/index.d.ts +4 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +3 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/progress-formatter.d.ts +5 -0
- package/dist/agent/progress-formatter.d.ts.map +1 -0
- package/dist/agent/progress-formatter.js +53 -0
- package/dist/agent/progress-formatter.js.map +1 -0
- package/dist/agent/run-queue.d.ts +8 -0
- package/dist/agent/run-queue.d.ts.map +1 -0
- package/dist/agent/run-queue.js +27 -0
- package/dist/agent/run-queue.js.map +1 -0
- package/dist/agent/runner-factory.d.ts +4 -0
- package/dist/agent/runner-factory.d.ts.map +1 -0
- package/dist/agent/runner-factory.js +11 -0
- package/dist/agent/runner-factory.js.map +1 -0
- package/dist/agent/session-events.d.ts +15 -0
- package/dist/agent/session-events.d.ts.map +1 -0
- package/dist/agent/session-events.js +216 -0
- package/dist/agent/session-events.js.map +1 -0
- package/dist/agent/type-guards.d.ts +23 -0
- package/dist/agent/type-guards.d.ts.map +1 -0
- package/dist/agent/type-guards.js +107 -0
- package/dist/agent/type-guards.js.map +1 -0
- package/dist/agent/types.d.ts +161 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +23 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/agent.d.ts +2 -15
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +1 -781
- package/dist/agent.js.map +1 -1
- package/dist/context.d.ts +58 -14
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +50 -7
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -12
- package/dist/index.js.map +1 -1
- package/dist/main.js +5 -404
- package/dist/main.js.map +1 -1
- package/dist/memory/bootstrap.d.ts +7 -0
- package/dist/memory/bootstrap.d.ts.map +1 -0
- package/dist/memory/bootstrap.js +47 -0
- package/dist/memory/bootstrap.js.map +1 -0
- package/dist/{memory-candidates.d.ts → memory/candidates.d.ts} +2 -1
- package/dist/memory/candidates.d.ts.map +1 -0
- package/dist/{memory-candidates.js → memory/candidates.js} +34 -21
- package/dist/memory/candidates.js.map +1 -0
- package/dist/memory/chinese-words.d.ts +2 -0
- package/dist/memory/chinese-words.d.ts.map +1 -0
- package/dist/memory/chinese-words.js +210 -0
- package/dist/memory/chinese-words.js.map +1 -0
- package/dist/{memory-consolidation.d.ts → memory/consolidation.d.ts} +1 -1
- package/dist/memory/consolidation.d.ts.map +1 -0
- package/dist/{memory-consolidation.js → memory/consolidation.js} +27 -35
- package/dist/memory/consolidation.js.map +1 -0
- package/dist/{memory-files.d.ts → memory/files.d.ts} +1 -6
- package/dist/memory/files.d.ts.map +1 -0
- package/dist/{memory-files.js → memory/files.js} +12 -36
- package/dist/memory/files.js.map +1 -0
- package/dist/{memory-lifecycle.d.ts → memory/lifecycle.d.ts} +24 -6
- package/dist/memory/lifecycle.d.ts.map +1 -0
- package/dist/memory/lifecycle.js +247 -0
- package/dist/memory/lifecycle.js.map +1 -0
- package/dist/{memory-recall.d.ts → memory/recall.d.ts} +2 -2
- package/dist/memory/recall.d.ts.map +1 -0
- package/dist/memory/recall.js +435 -0
- package/dist/memory/recall.js.map +1 -0
- package/dist/{session-memory.d.ts → memory/session.d.ts} +2 -1
- package/dist/memory/session.d.ts.map +1 -0
- package/dist/{session-memory.js → memory/session.js} +32 -62
- package/dist/memory/session.js.map +1 -0
- package/dist/runtime/bootstrap.d.ts +48 -0
- package/dist/runtime/bootstrap.d.ts.map +1 -0
- package/dist/runtime/bootstrap.js +451 -0
- package/dist/runtime/bootstrap.js.map +1 -0
- package/dist/runtime/delivery.d.ts.map +1 -0
- package/dist/{delivery.js → runtime/delivery.js} +1 -1
- package/dist/runtime/delivery.js.map +1 -0
- package/dist/{dingtalk.d.ts → runtime/dingtalk.d.ts} +10 -0
- package/dist/runtime/dingtalk.d.ts.map +1 -0
- package/dist/{dingtalk.js → runtime/dingtalk.js} +87 -27
- package/dist/runtime/dingtalk.js.map +1 -0
- package/dist/runtime/events.d.ts.map +1 -0
- package/dist/{events.js → runtime/events.js} +1 -1
- package/dist/runtime/events.js.map +1 -0
- package/dist/{store.d.ts → runtime/store.d.ts} +5 -0
- package/dist/runtime/store.d.ts.map +1 -0
- package/dist/{store.js → runtime/store.js} +60 -19
- package/dist/runtime/store.js.map +1 -0
- package/dist/shared/markdown-sections.d.ts +7 -0
- package/dist/shared/markdown-sections.d.ts.map +1 -0
- package/dist/{markdown-sections.js → shared/markdown-sections.js} +10 -3
- package/dist/shared/markdown-sections.js.map +1 -0
- package/dist/shared/text-utils.d.ts +10 -0
- package/dist/shared/text-utils.d.ts.map +1 -0
- package/dist/shared/text-utils.js +37 -0
- package/dist/shared/text-utils.js.map +1 -0
- package/dist/shared/type-guards.d.ts +6 -0
- package/dist/shared/type-guards.d.ts.map +1 -0
- package/dist/shared/type-guards.js +13 -0
- package/dist/shared/type-guards.js.map +1 -0
- package/dist/shared/types.d.ts +15 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +2 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/sidecar-worker.d.ts.map +1 -1
- package/dist/sidecar-worker.js +1 -7
- package/dist/sidecar-worker.js.map +1 -1
- package/dist/{sub-agents.d.ts → subagents/discovery.d.ts} +1 -1
- package/dist/subagents/discovery.d.ts.map +1 -0
- package/dist/{sub-agents.js → subagents/discovery.js} +3 -3
- package/dist/subagents/discovery.js.map +1 -0
- package/dist/{tools/subagent.d.ts → subagents/tool.d.ts} +3 -16
- package/dist/{tools/subagent.d.ts.map → subagents/tool.d.ts.map} +1 -1
- package/dist/{tools/subagent.js → subagents/tool.js} +17 -38
- package/dist/subagents/tool.js.map +1 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/index.js.map +1 -1
- package/docs/memory-audit.md +330 -0
- package/docs/memory-optimization-round2.md +319 -0
- package/package.json +1 -1
- package/dist/delivery.d.ts.map +0 -1
- package/dist/delivery.js.map +0 -1
- package/dist/dingtalk.d.ts.map +0 -1
- package/dist/dingtalk.js.map +0 -1
- package/dist/events.d.ts.map +0 -1
- package/dist/events.js.map +0 -1
- package/dist/markdown-sections.d.ts +0 -6
- package/dist/markdown-sections.d.ts.map +0 -1
- package/dist/markdown-sections.js.map +0 -1
- package/dist/memory-candidates.d.ts.map +0 -1
- package/dist/memory-candidates.js.map +0 -1
- package/dist/memory-consolidation.d.ts.map +0 -1
- package/dist/memory-consolidation.js.map +0 -1
- package/dist/memory-files.d.ts.map +0 -1
- package/dist/memory-files.js.map +0 -1
- package/dist/memory-lifecycle.d.ts.map +0 -1
- package/dist/memory-lifecycle.js +0 -150
- package/dist/memory-lifecycle.js.map +0 -1
- package/dist/memory-recall.d.ts.map +0 -1
- package/dist/memory-recall.js +0 -218
- package/dist/memory-recall.js.map +0 -1
- package/dist/session-memory-files.d.ts +0 -2
- package/dist/session-memory-files.d.ts.map +0 -1
- package/dist/session-memory-files.js +0 -2
- package/dist/session-memory-files.js.map +0 -1
- package/dist/session-memory.d.ts.map +0 -1
- package/dist/session-memory.js.map +0 -1
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js.map +0 -1
- package/dist/sub-agents.d.ts.map +0 -1
- package/dist/sub-agents.js.map +0 -1
- package/dist/tools/subagent.js.map +0 -1
- package/docs/proj-review.md +0 -188
- package/docs/test-supplementation-plan.md +0 -553
- /package/dist/{delivery.d.ts → runtime/delivery.d.ts} +0 -0
- /package/dist/{events.d.ts → runtime/events.d.ts} +0 -0
- /package/docs/{memory-rfc.md → specs/001-implement-memory/memory-rfc.md} +0 -0
- /package/docs/{subagent → specs/002-subagent}/pi-subagent-analyse.txt +0 -0
- /package/docs/{subagent → specs/002-subagent}/pi-subagent-design.txt +0 -0
- /package/docs/{subagent → specs/002-subagent}/pi-subagent-phase1-plan.txt +0 -0
- /package/docs/{improve-memory → specs/003-improve-memory}/design.md +0 -0
- /package/docs/{improve-memory → specs/003-improve-memory}/interfaces-and-tests.md +0 -0
- /package/docs/{improve-memory → specs/003-improve-memory}/spec.md +0 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { getOrCreateRunner } from "../agent.js";
|
|
4
|
+
import { parseBuiltInCommand } from "../commands.js";
|
|
5
|
+
import * as log from "../log.js";
|
|
6
|
+
import { ensureChannelMemoryFilesSync } from "../memory/files.js";
|
|
7
|
+
import { APP_HOME_DIR, APP_NAME, AUTH_CONFIG_PATH, CHANNEL_CONFIG_PATH, MODELS_CONFIG_PATH, SETTINGS_CONFIG_PATH, WORKSPACE_DIR, } from "../paths.js";
|
|
8
|
+
import { parseSandboxArg, validateSandbox } from "../sandbox.js";
|
|
9
|
+
import { createDingTalkContext } from "./delivery.js";
|
|
10
|
+
import { DingTalkBot, } from "./dingtalk.js";
|
|
11
|
+
import { createEventsWatcher } from "./events.js";
|
|
12
|
+
import { ChannelStore } from "./store.js";
|
|
13
|
+
const DEFAULT_SOUL = `# SOUL.md
|
|
14
|
+
|
|
15
|
+
Configure Pipiclaw's identity, voice, and communication style here.
|
|
16
|
+
|
|
17
|
+
Suggested sections:
|
|
18
|
+
|
|
19
|
+
- Who the assistant is
|
|
20
|
+
- Default language
|
|
21
|
+
- Tone and personality
|
|
22
|
+
- Reply style
|
|
23
|
+
- Formatting preferences
|
|
24
|
+
|
|
25
|
+
Example topics you may want to define:
|
|
26
|
+
|
|
27
|
+
- "Answer in Chinese by default."
|
|
28
|
+
- "Be concise and direct."
|
|
29
|
+
- "Prefer Markdown."
|
|
30
|
+
- "Act as an engineering assistant for our team."
|
|
31
|
+
|
|
32
|
+
Replace this template with your actual identity prompt.
|
|
33
|
+
`;
|
|
34
|
+
const DEFAULT_AGENT = `# AGENTS.md
|
|
35
|
+
|
|
36
|
+
Configure Pipiclaw's operating rules here.
|
|
37
|
+
|
|
38
|
+
This file should define behavior and workflow. Identity, tone, and personality belong in \`SOUL.md\`.
|
|
39
|
+
|
|
40
|
+
Suggested sections:
|
|
41
|
+
|
|
42
|
+
- Tool usage policy
|
|
43
|
+
- Security constraints
|
|
44
|
+
- Scheduling/reminder policy
|
|
45
|
+
- Project-specific workflows
|
|
46
|
+
- Things the assistant must always or never do
|
|
47
|
+
|
|
48
|
+
Replace this template with your actual operating instructions.
|
|
49
|
+
`;
|
|
50
|
+
const DEFAULT_MEMORY = `# Workspace Memory
|
|
51
|
+
|
|
52
|
+
This file stores stable workspace-level memory.
|
|
53
|
+
|
|
54
|
+
- It is intended to be managed by a human administrator.
|
|
55
|
+
- It is not automatically rewritten by normal runtime consolidation.
|
|
56
|
+
- Store durable shared background here when it should apply across channels.
|
|
57
|
+
- Keep this file focused on stable facts, policies, and shared context, not transient conversation history.
|
|
58
|
+
|
|
59
|
+
## Shared Context
|
|
60
|
+
|
|
61
|
+
<!-- Put team-wide or workspace-wide background here. -->
|
|
62
|
+
|
|
63
|
+
## Tooling And Environment
|
|
64
|
+
|
|
65
|
+
<!-- Put durable tool usage rules, environment assumptions, or shared operational conventions here. -->
|
|
66
|
+
|
|
67
|
+
## Project Notes
|
|
68
|
+
|
|
69
|
+
<!-- Put long-lived project facts here. -->
|
|
70
|
+
`;
|
|
71
|
+
const CHANNEL_CONFIG_TEMPLATE = {
|
|
72
|
+
clientId: "your-dingtalk-client-id",
|
|
73
|
+
clientSecret: "your-dingtalk-client-secret",
|
|
74
|
+
robotCode: "your-robot-code",
|
|
75
|
+
cardTemplateId: "your-card-template-id",
|
|
76
|
+
cardTemplateKey: "content",
|
|
77
|
+
allowFrom: ["your-staff-id"],
|
|
78
|
+
};
|
|
79
|
+
const MODELS_CONFIG_TEMPLATE = { providers: {} };
|
|
80
|
+
const SHUTDOWN_WAIT_MS = 15000;
|
|
81
|
+
const SHUTDOWN_ABORT_WAIT_MS = 5000;
|
|
82
|
+
export const DEFAULT_BOOTSTRAP_PATHS = {
|
|
83
|
+
appName: APP_NAME,
|
|
84
|
+
appHomeDir: APP_HOME_DIR,
|
|
85
|
+
workspaceDir: WORKSPACE_DIR,
|
|
86
|
+
authConfigPath: AUTH_CONFIG_PATH,
|
|
87
|
+
channelConfigPath: CHANNEL_CONFIG_PATH,
|
|
88
|
+
modelsConfigPath: MODELS_CONFIG_PATH,
|
|
89
|
+
settingsConfigPath: SETTINGS_CONFIG_PATH,
|
|
90
|
+
};
|
|
91
|
+
export class BootstrapExitError extends Error {
|
|
92
|
+
constructor(code, message) {
|
|
93
|
+
super(message ?? `Bootstrap requested exit with code ${code}`);
|
|
94
|
+
this.code = code;
|
|
95
|
+
this.name = "BootstrapExitError";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function isBootstrapExitError(error) {
|
|
99
|
+
return error instanceof BootstrapExitError;
|
|
100
|
+
}
|
|
101
|
+
export function sanitizeProxyEnv(env) {
|
|
102
|
+
if (env.DINGTALK_FORCE_PROXY !== "true") {
|
|
103
|
+
delete env.http_proxy;
|
|
104
|
+
delete env.https_proxy;
|
|
105
|
+
delete env.all_proxy;
|
|
106
|
+
delete env.HTTP_PROXY;
|
|
107
|
+
delete env.HTTPS_PROXY;
|
|
108
|
+
delete env.ALL_PROXY;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function writeTextFileIfMissing(path, content, label, created) {
|
|
112
|
+
if (existsSync(path)) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
writeFileSync(path, content, "utf-8");
|
|
116
|
+
created.push(label);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
function writeJsonFileIfMissing(path, value, label, created) {
|
|
120
|
+
return writeTextFileIfMissing(path, `${JSON.stringify(value, null, 2)}\n`, label, created);
|
|
121
|
+
}
|
|
122
|
+
export function bootstrapAppHome(paths = DEFAULT_BOOTSTRAP_PATHS) {
|
|
123
|
+
const created = [];
|
|
124
|
+
if (!existsSync(paths.appHomeDir)) {
|
|
125
|
+
mkdirSync(paths.appHomeDir, { recursive: true });
|
|
126
|
+
created.push("app home");
|
|
127
|
+
}
|
|
128
|
+
if (!existsSync(paths.workspaceDir)) {
|
|
129
|
+
mkdirSync(paths.workspaceDir, { recursive: true });
|
|
130
|
+
created.push("workspace/");
|
|
131
|
+
}
|
|
132
|
+
for (const dir of ["skills", "events", "sub-agents"]) {
|
|
133
|
+
const dirPath = join(paths.workspaceDir, dir);
|
|
134
|
+
if (!existsSync(dirPath)) {
|
|
135
|
+
mkdirSync(dirPath, { recursive: true });
|
|
136
|
+
created.push(`workspace/${dir}/`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
writeTextFileIfMissing(join(paths.workspaceDir, "SOUL.md"), DEFAULT_SOUL, "workspace/SOUL.md", created);
|
|
140
|
+
writeTextFileIfMissing(join(paths.workspaceDir, "AGENTS.md"), DEFAULT_AGENT, "workspace/AGENTS.md", created);
|
|
141
|
+
writeTextFileIfMissing(join(paths.workspaceDir, "MEMORY.md"), DEFAULT_MEMORY, "workspace/MEMORY.md", created);
|
|
142
|
+
const channelTemplateCreated = writeJsonFileIfMissing(paths.channelConfigPath, CHANNEL_CONFIG_TEMPLATE, "channel.json", created);
|
|
143
|
+
writeJsonFileIfMissing(paths.authConfigPath, {}, "auth.json", created);
|
|
144
|
+
writeJsonFileIfMissing(paths.modelsConfigPath, MODELS_CONFIG_TEMPLATE, "models.json", created);
|
|
145
|
+
writeJsonFileIfMissing(paths.settingsConfigPath, {}, "settings.json", created);
|
|
146
|
+
return { created, channelTemplateCreated };
|
|
147
|
+
}
|
|
148
|
+
function isPlaceholderString(value) {
|
|
149
|
+
return value.trim().startsWith("your-");
|
|
150
|
+
}
|
|
151
|
+
function listChannelConfigIssues(config) {
|
|
152
|
+
const issues = [];
|
|
153
|
+
if (!config.clientId) {
|
|
154
|
+
issues.push("Missing required field `clientId`.");
|
|
155
|
+
}
|
|
156
|
+
else if (isPlaceholderString(config.clientId)) {
|
|
157
|
+
issues.push("Replace placeholder value for `clientId`.");
|
|
158
|
+
}
|
|
159
|
+
if (!config.clientSecret) {
|
|
160
|
+
issues.push("Missing required field `clientSecret`.");
|
|
161
|
+
}
|
|
162
|
+
else if (isPlaceholderString(config.clientSecret)) {
|
|
163
|
+
issues.push("Replace placeholder value for `clientSecret`.");
|
|
164
|
+
}
|
|
165
|
+
if (config.robotCode && isPlaceholderString(config.robotCode)) {
|
|
166
|
+
issues.push("Replace placeholder value for `robotCode`, or set it to an empty string to reuse `clientId`.");
|
|
167
|
+
}
|
|
168
|
+
if (config.cardTemplateId && isPlaceholderString(config.cardTemplateId)) {
|
|
169
|
+
issues.push("Replace placeholder value for `cardTemplateId`, or set it to an empty string to disable AI Card streaming.");
|
|
170
|
+
}
|
|
171
|
+
if (Array.isArray(config.allowFrom) && config.allowFrom.some((value) => isPlaceholderString(value))) {
|
|
172
|
+
issues.push("Replace placeholder values in `allowFrom`, or set it to an empty array to allow all users.");
|
|
173
|
+
}
|
|
174
|
+
return issues;
|
|
175
|
+
}
|
|
176
|
+
export function printBootstrapSummary(result, io = console, paths = DEFAULT_BOOTSTRAP_PATHS) {
|
|
177
|
+
if (result.created.length === 0) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
io.log(`Initialized ${paths.appName} under ${paths.appHomeDir}:`);
|
|
181
|
+
for (const item of result.created) {
|
|
182
|
+
io.log(` - ${item}`);
|
|
183
|
+
}
|
|
184
|
+
io.log("");
|
|
185
|
+
}
|
|
186
|
+
export function loadConfig(paths = DEFAULT_BOOTSTRAP_PATHS, io = console) {
|
|
187
|
+
let parsed;
|
|
188
|
+
try {
|
|
189
|
+
parsed = JSON.parse(readFileSync(paths.channelConfigPath, "utf-8"));
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
io.error(`Failed to parse configuration: ${paths.channelConfigPath}`);
|
|
193
|
+
io.error(err instanceof Error ? err.message : String(err));
|
|
194
|
+
throw new BootstrapExitError(1);
|
|
195
|
+
}
|
|
196
|
+
const issues = listChannelConfigIssues(parsed);
|
|
197
|
+
if (issues.length > 0) {
|
|
198
|
+
io.error(`Configuration is not ready: ${paths.channelConfigPath}`);
|
|
199
|
+
for (const issue of issues) {
|
|
200
|
+
io.error(` - ${issue}`);
|
|
201
|
+
}
|
|
202
|
+
io.error("");
|
|
203
|
+
io.error(`Fill in ${paths.channelConfigPath} and run \`${paths.appName}\` again.`);
|
|
204
|
+
throw new BootstrapExitError(1);
|
|
205
|
+
}
|
|
206
|
+
parsed.cardTemplateKey = parsed.cardTemplateKey || "content";
|
|
207
|
+
parsed.robotCode = parsed.robotCode?.trim() ? parsed.robotCode : parsed.clientId;
|
|
208
|
+
if (Array.isArray(parsed.allowFrom)) {
|
|
209
|
+
parsed.allowFrom = parsed.allowFrom.filter((value) => value.trim().length > 0);
|
|
210
|
+
}
|
|
211
|
+
return parsed;
|
|
212
|
+
}
|
|
213
|
+
export function parseArgs(argv, paths = DEFAULT_BOOTSTRAP_PATHS, io = console) {
|
|
214
|
+
const args = argv.slice(2);
|
|
215
|
+
let sandbox = { type: "host" };
|
|
216
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
217
|
+
const arg = args[index];
|
|
218
|
+
if (arg.startsWith("--sandbox=")) {
|
|
219
|
+
sandbox = parseSandboxArg(arg.slice("--sandbox=".length));
|
|
220
|
+
}
|
|
221
|
+
else if (arg === "--sandbox") {
|
|
222
|
+
sandbox = parseSandboxArg(args[index + 1] || "");
|
|
223
|
+
index += 1;
|
|
224
|
+
}
|
|
225
|
+
else if (arg === "--help" || arg === "-h") {
|
|
226
|
+
io.log(`Usage: ${paths.appName} [options]`);
|
|
227
|
+
io.log("");
|
|
228
|
+
io.log("Options:");
|
|
229
|
+
io.log(" --sandbox=host Run tools on host (default)");
|
|
230
|
+
io.log(" --sandbox=docker:<name> Run tools in Docker container");
|
|
231
|
+
io.log("");
|
|
232
|
+
io.log(`Config: ${paths.channelConfigPath}`);
|
|
233
|
+
io.log(`Workspace: ${paths.workspaceDir}`);
|
|
234
|
+
throw new BootstrapExitError(0);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { sandbox };
|
|
238
|
+
}
|
|
239
|
+
function waitForTasks(tasks, timeoutMs) {
|
|
240
|
+
if (tasks.length === 0) {
|
|
241
|
+
return Promise.resolve(true);
|
|
242
|
+
}
|
|
243
|
+
return Promise.race([
|
|
244
|
+
Promise.allSettled(tasks).then(() => true),
|
|
245
|
+
new Promise((resolve) => {
|
|
246
|
+
setTimeout(() => resolve(false), timeoutMs);
|
|
247
|
+
}),
|
|
248
|
+
]);
|
|
249
|
+
}
|
|
250
|
+
export async function bootstrap(argv, options = {}) {
|
|
251
|
+
const env = options.env ?? process.env;
|
|
252
|
+
const io = options.io ?? console;
|
|
253
|
+
const paths = options.paths ?? DEFAULT_BOOTSTRAP_PATHS;
|
|
254
|
+
const registerSignalHandlers = options.registerSignalHandlers ?? true;
|
|
255
|
+
const startServices = options.startServices ?? true;
|
|
256
|
+
sanitizeProxyEnv(env);
|
|
257
|
+
const parsedArgs = parseArgs(argv, paths, io);
|
|
258
|
+
const sandbox = parsedArgs.sandbox;
|
|
259
|
+
const bootstrapResult = bootstrapAppHome(paths);
|
|
260
|
+
printBootstrapSummary(bootstrapResult, io, paths);
|
|
261
|
+
if (bootstrapResult.channelTemplateCreated) {
|
|
262
|
+
io.error(`Fill in ${paths.channelConfigPath} and run \`${paths.appName}\` again.`);
|
|
263
|
+
throw new BootstrapExitError(1);
|
|
264
|
+
}
|
|
265
|
+
const dingtalkConfig = loadConfig(paths, io);
|
|
266
|
+
dingtalkConfig.stateDir = paths.workspaceDir;
|
|
267
|
+
await validateSandbox(sandbox);
|
|
268
|
+
const store = new ChannelStore({ workingDir: paths.workspaceDir });
|
|
269
|
+
const channelStates = new Map();
|
|
270
|
+
const activeTasks = new Set();
|
|
271
|
+
let shuttingDown = false;
|
|
272
|
+
let shutdownPromise = null;
|
|
273
|
+
const getState = (channelId) => {
|
|
274
|
+
let state = channelStates.get(channelId);
|
|
275
|
+
if (!state) {
|
|
276
|
+
const channelDir = join(paths.workspaceDir, channelId);
|
|
277
|
+
ensureChannelMemoryFilesSync(channelDir);
|
|
278
|
+
state = {
|
|
279
|
+
running: false,
|
|
280
|
+
runner: getOrCreateRunner(sandbox, channelId, channelDir),
|
|
281
|
+
stopRequested: false,
|
|
282
|
+
};
|
|
283
|
+
channelStates.set(channelId, state);
|
|
284
|
+
}
|
|
285
|
+
return state;
|
|
286
|
+
};
|
|
287
|
+
const handler = {
|
|
288
|
+
isRunning(channelId) {
|
|
289
|
+
const state = channelStates.get(channelId);
|
|
290
|
+
return state?.running ?? false;
|
|
291
|
+
},
|
|
292
|
+
async handleStop(channelId, _bot) {
|
|
293
|
+
const state = channelStates.get(channelId);
|
|
294
|
+
if (state?.running) {
|
|
295
|
+
state.stopRequested = true;
|
|
296
|
+
void state.runner.abort().catch((err) => {
|
|
297
|
+
log.logWarning(`[${channelId}] Failed to abort run`, err instanceof Error ? err.message : String(err));
|
|
298
|
+
});
|
|
299
|
+
log.logInfo(`[${channelId}] Stop requested`);
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
async handleBusyMessage(event, bot, mode, queueText) {
|
|
303
|
+
if (shuttingDown) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const state = getState(event.channelId);
|
|
307
|
+
const trimmedQueueText = queueText.trim();
|
|
308
|
+
await store.logMessage(event.channelId, {
|
|
309
|
+
date: new Date().toISOString(),
|
|
310
|
+
ts: event.ts,
|
|
311
|
+
user: event.user,
|
|
312
|
+
userName: event.userName,
|
|
313
|
+
text: event.text,
|
|
314
|
+
isBot: false,
|
|
315
|
+
deliveryMode: mode,
|
|
316
|
+
skipContextSync: true,
|
|
317
|
+
});
|
|
318
|
+
try {
|
|
319
|
+
if (mode === "followUp") {
|
|
320
|
+
await state.runner.queueFollowUp(trimmedQueueText, event.userName);
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
await state.runner.queueSteer(trimmedQueueText, event.userName);
|
|
324
|
+
}
|
|
325
|
+
const confirmation = mode === "followUp"
|
|
326
|
+
? "Queued as follow-up. I’ll handle it after the current task completes."
|
|
327
|
+
: event.text.trim().startsWith("/")
|
|
328
|
+
? "Queued as steer. I’ll apply it after the current tool step finishes."
|
|
329
|
+
: "Queued as steer. I’ll apply this after the current tool step finishes. Use `/followup <message>` to queue it after completion.";
|
|
330
|
+
await bot.sendPlain(event.channelId, confirmation);
|
|
331
|
+
log.logInfo(`[${event.channelId}] Queued ${mode}: ${trimmedQueueText.substring(0, 80)}`);
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
335
|
+
log.logWarning(`[${event.channelId}] Failed to queue ${mode}`, errMsg);
|
|
336
|
+
await bot.sendPlain(event.channelId, `Could not queue this message: ${errMsg}`);
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
async handleEvent(event, bot, _isEvent) {
|
|
340
|
+
if (shuttingDown) {
|
|
341
|
+
log.logInfo(`[${event.channelId}] Ignoring event during shutdown`);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const state = getState(event.channelId);
|
|
345
|
+
const task = (async () => {
|
|
346
|
+
state.running = true;
|
|
347
|
+
state.stopRequested = false;
|
|
348
|
+
await store.logMessage(event.channelId, {
|
|
349
|
+
date: new Date().toISOString(),
|
|
350
|
+
ts: event.ts,
|
|
351
|
+
user: event.user,
|
|
352
|
+
userName: event.userName,
|
|
353
|
+
text: event.text,
|
|
354
|
+
isBot: false,
|
|
355
|
+
});
|
|
356
|
+
try {
|
|
357
|
+
const ctx = createDingTalkContext(event, bot, store);
|
|
358
|
+
const builtInCommand = parseBuiltInCommand(event.text);
|
|
359
|
+
if (builtInCommand) {
|
|
360
|
+
log.logInfo(`[${event.channelId}] Executing command: ${builtInCommand.rawText}`);
|
|
361
|
+
await state.runner.handleBuiltinCommand(ctx, builtInCommand);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
log.logInfo(`[${event.channelId}] Starting run: ${event.text.substring(0, 50)}`);
|
|
365
|
+
const result = await state.runner.run(ctx, store);
|
|
366
|
+
if (result.stopReason === "aborted" && state.stopRequested) {
|
|
367
|
+
log.logInfo(`[${event.channelId}] Stopped`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
log.logWarning(`[${event.channelId}] Run error`, err instanceof Error ? err.message : String(err));
|
|
372
|
+
}
|
|
373
|
+
finally {
|
|
374
|
+
state.running = false;
|
|
375
|
+
}
|
|
376
|
+
})();
|
|
377
|
+
activeTasks.add(task);
|
|
378
|
+
try {
|
|
379
|
+
await task;
|
|
380
|
+
}
|
|
381
|
+
finally {
|
|
382
|
+
activeTasks.delete(task);
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
log.logStartup(paths.workspaceDir, sandbox.type === "host" ? "host" : `docker:${sandbox.container}`);
|
|
387
|
+
const bot = new DingTalkBot(handler, dingtalkConfig);
|
|
388
|
+
const eventsWatcher = createEventsWatcher(paths.workspaceDir, bot);
|
|
389
|
+
const shutdownWithReason = async (reason) => {
|
|
390
|
+
if (shutdownPromise) {
|
|
391
|
+
return shutdownPromise;
|
|
392
|
+
}
|
|
393
|
+
shutdownPromise = (async () => {
|
|
394
|
+
shuttingDown = true;
|
|
395
|
+
log.logInfo(`Shutting down (${reason})...`);
|
|
396
|
+
eventsWatcher.stop();
|
|
397
|
+
await bot.stop();
|
|
398
|
+
const runningTasks = Array.from(activeTasks);
|
|
399
|
+
if (runningTasks.length > 0) {
|
|
400
|
+
log.logInfo(`Waiting for ${runningTasks.length} active task(s) to finish`);
|
|
401
|
+
const completed = await waitForTasks(runningTasks, SHUTDOWN_WAIT_MS);
|
|
402
|
+
if (!completed) {
|
|
403
|
+
log.logWarning(`Shutdown grace period exceeded ${SHUTDOWN_WAIT_MS}ms, aborting active runs`);
|
|
404
|
+
const aborts = [];
|
|
405
|
+
for (const [channelId, state] of channelStates) {
|
|
406
|
+
if (!state.running)
|
|
407
|
+
continue;
|
|
408
|
+
state.stopRequested = true;
|
|
409
|
+
log.logInfo(`[${channelId}] Aborting active run for shutdown`);
|
|
410
|
+
aborts.push(state.runner.abort().catch((err) => {
|
|
411
|
+
log.logWarning(`[${channelId}] Failed to abort run during shutdown`, err instanceof Error ? err.message : String(err));
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
await Promise.allSettled(aborts);
|
|
415
|
+
const remainingTasks = Array.from(activeTasks);
|
|
416
|
+
if (remainingTasks.length > 0) {
|
|
417
|
+
const abortedCompleted = await waitForTasks(remainingTasks, SHUTDOWN_ABORT_WAIT_MS);
|
|
418
|
+
if (!abortedCompleted) {
|
|
419
|
+
log.logWarning(`Shutdown forced exit with ${remainingTasks.length} task(s) still active`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
})();
|
|
425
|
+
return shutdownPromise;
|
|
426
|
+
};
|
|
427
|
+
if (registerSignalHandlers) {
|
|
428
|
+
process.once("SIGINT", () => {
|
|
429
|
+
void shutdownWithReason("SIGINT").finally(() => {
|
|
430
|
+
process.exit(0);
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
process.once("SIGTERM", () => {
|
|
434
|
+
void shutdownWithReason("SIGTERM").finally(() => {
|
|
435
|
+
process.exit(0);
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
if (startServices) {
|
|
440
|
+
eventsWatcher.start();
|
|
441
|
+
void bot.start();
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
bot,
|
|
445
|
+
store,
|
|
446
|
+
shutdown: async () => {
|
|
447
|
+
await shutdownWithReason("manual");
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/runtime/bootstrap.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { type AgentRunner, getOrCreateRunner } from \"../agent.js\";\nimport { parseBuiltInCommand } from \"../commands.js\";\nimport * as log from \"../log.js\";\nimport { ensureChannelMemoryFilesSync } from \"../memory/files.js\";\nimport {\n\tAPP_HOME_DIR,\n\tAPP_NAME,\n\tAUTH_CONFIG_PATH,\n\tCHANNEL_CONFIG_PATH,\n\tMODELS_CONFIG_PATH,\n\tSETTINGS_CONFIG_PATH,\n\tWORKSPACE_DIR,\n} from \"../paths.js\";\nimport { parseSandboxArg, type SandboxConfig, validateSandbox } from \"../sandbox.js\";\nimport { createDingTalkContext } from \"./delivery.js\";\nimport {\n\ttype BusyMessageMode,\n\tDingTalkBot,\n\ttype DingTalkConfig,\n\ttype DingTalkEvent,\n\ttype DingTalkHandler,\n} from \"./dingtalk.js\";\nimport { createEventsWatcher } from \"./events.js\";\nimport { ChannelStore } from \"./store.js\";\n\nexport interface BootstrapPaths {\n\tappName: string;\n\tappHomeDir: string;\n\tworkspaceDir: string;\n\tauthConfigPath: string;\n\tchannelConfigPath: string;\n\tmodelsConfigPath: string;\n\tsettingsConfigPath: string;\n}\n\nexport interface BootstrapIO {\n\tlog: (...args: unknown[]) => void;\n\terror: (...args: unknown[]) => void;\n}\n\nexport interface BootstrapOptions {\n\tenv?: NodeJS.ProcessEnv;\n\tio?: BootstrapIO;\n\tpaths?: BootstrapPaths;\n\tregisterSignalHandlers?: boolean;\n\tstartServices?: boolean;\n}\n\nexport interface ParsedArgs {\n\tsandbox: SandboxConfig;\n}\n\nexport interface BootstrapResult {\n\tcreated: string[];\n\tchannelTemplateCreated: boolean;\n}\n\nexport interface AppContext {\n\tbot: DingTalkBot;\n\tstore: ChannelStore;\n\tshutdown: () => Promise<void>;\n}\n\ninterface ChannelState {\n\trunning: boolean;\n\trunner: AgentRunner;\n\tstopRequested: boolean;\n}\n\nconst DEFAULT_SOUL = `# SOUL.md\n\nConfigure Pipiclaw's identity, voice, and communication style here.\n\nSuggested sections:\n\n- Who the assistant is\n- Default language\n- Tone and personality\n- Reply style\n- Formatting preferences\n\nExample topics you may want to define:\n\n- \"Answer in Chinese by default.\"\n- \"Be concise and direct.\"\n- \"Prefer Markdown.\"\n- \"Act as an engineering assistant for our team.\"\n\nReplace this template with your actual identity prompt.\n`;\n\nconst DEFAULT_AGENT = `# AGENTS.md\n\nConfigure Pipiclaw's operating rules here.\n\nThis file should define behavior and workflow. Identity, tone, and personality belong in \\`SOUL.md\\`.\n\nSuggested sections:\n\n- Tool usage policy\n- Security constraints\n- Scheduling/reminder policy\n- Project-specific workflows\n- Things the assistant must always or never do\n\nReplace this template with your actual operating instructions.\n`;\n\nconst DEFAULT_MEMORY = `# Workspace Memory\n\nThis file stores stable workspace-level memory.\n\n- It is intended to be managed by a human administrator.\n- It is not automatically rewritten by normal runtime consolidation.\n- Store durable shared background here when it should apply across channels.\n- Keep this file focused on stable facts, policies, and shared context, not transient conversation history.\n\n## Shared Context\n\n<!-- Put team-wide or workspace-wide background here. -->\n\n## Tooling And Environment\n\n<!-- Put durable tool usage rules, environment assumptions, or shared operational conventions here. -->\n\n## Project Notes\n\n<!-- Put long-lived project facts here. -->\n`;\n\nconst CHANNEL_CONFIG_TEMPLATE = {\n\tclientId: \"your-dingtalk-client-id\",\n\tclientSecret: \"your-dingtalk-client-secret\",\n\trobotCode: \"your-robot-code\",\n\tcardTemplateId: \"your-card-template-id\",\n\tcardTemplateKey: \"content\",\n\tallowFrom: [\"your-staff-id\"],\n} satisfies DingTalkConfig;\n\nconst MODELS_CONFIG_TEMPLATE = { providers: {} };\n\nconst SHUTDOWN_WAIT_MS = 15000;\nconst SHUTDOWN_ABORT_WAIT_MS = 5000;\n\nexport const DEFAULT_BOOTSTRAP_PATHS: BootstrapPaths = {\n\tappName: APP_NAME,\n\tappHomeDir: APP_HOME_DIR,\n\tworkspaceDir: WORKSPACE_DIR,\n\tauthConfigPath: AUTH_CONFIG_PATH,\n\tchannelConfigPath: CHANNEL_CONFIG_PATH,\n\tmodelsConfigPath: MODELS_CONFIG_PATH,\n\tsettingsConfigPath: SETTINGS_CONFIG_PATH,\n};\n\nexport class BootstrapExitError extends Error {\n\treadonly code: number;\n\n\tconstructor(code: number, message?: string) {\n\t\tsuper(message ?? `Bootstrap requested exit with code ${code}`);\n\t\tthis.code = code;\n\t\tthis.name = \"BootstrapExitError\";\n\t}\n}\n\nexport function isBootstrapExitError(error: unknown): error is BootstrapExitError {\n\treturn error instanceof BootstrapExitError;\n}\n\nexport function sanitizeProxyEnv(env: NodeJS.ProcessEnv): void {\n\tif (env.DINGTALK_FORCE_PROXY !== \"true\") {\n\t\tdelete env.http_proxy;\n\t\tdelete env.https_proxy;\n\t\tdelete env.all_proxy;\n\t\tdelete env.HTTP_PROXY;\n\t\tdelete env.HTTPS_PROXY;\n\t\tdelete env.ALL_PROXY;\n\t}\n}\n\nfunction writeTextFileIfMissing(path: string, content: string, label: string, created: string[]): boolean {\n\tif (existsSync(path)) {\n\t\treturn false;\n\t}\n\twriteFileSync(path, content, \"utf-8\");\n\tcreated.push(label);\n\treturn true;\n}\n\nfunction writeJsonFileIfMissing(path: string, value: unknown, label: string, created: string[]): boolean {\n\treturn writeTextFileIfMissing(path, `${JSON.stringify(value, null, 2)}\\n`, label, created);\n}\n\nexport function bootstrapAppHome(paths: BootstrapPaths = DEFAULT_BOOTSTRAP_PATHS): BootstrapResult {\n\tconst created: string[] = [];\n\n\tif (!existsSync(paths.appHomeDir)) {\n\t\tmkdirSync(paths.appHomeDir, { recursive: true });\n\t\tcreated.push(\"app home\");\n\t}\n\tif (!existsSync(paths.workspaceDir)) {\n\t\tmkdirSync(paths.workspaceDir, { recursive: true });\n\t\tcreated.push(\"workspace/\");\n\t}\n\n\tfor (const dir of [\"skills\", \"events\", \"sub-agents\"]) {\n\t\tconst dirPath = join(paths.workspaceDir, dir);\n\t\tif (!existsSync(dirPath)) {\n\t\t\tmkdirSync(dirPath, { recursive: true });\n\t\t\tcreated.push(`workspace/${dir}/`);\n\t\t}\n\t}\n\n\twriteTextFileIfMissing(join(paths.workspaceDir, \"SOUL.md\"), DEFAULT_SOUL, \"workspace/SOUL.md\", created);\n\twriteTextFileIfMissing(join(paths.workspaceDir, \"AGENTS.md\"), DEFAULT_AGENT, \"workspace/AGENTS.md\", created);\n\twriteTextFileIfMissing(join(paths.workspaceDir, \"MEMORY.md\"), DEFAULT_MEMORY, \"workspace/MEMORY.md\", created);\n\n\tconst channelTemplateCreated = writeJsonFileIfMissing(\n\t\tpaths.channelConfigPath,\n\t\tCHANNEL_CONFIG_TEMPLATE,\n\t\t\"channel.json\",\n\t\tcreated,\n\t);\n\twriteJsonFileIfMissing(paths.authConfigPath, {}, \"auth.json\", created);\n\twriteJsonFileIfMissing(paths.modelsConfigPath, MODELS_CONFIG_TEMPLATE, \"models.json\", created);\n\twriteJsonFileIfMissing(paths.settingsConfigPath, {}, \"settings.json\", created);\n\n\treturn { created, channelTemplateCreated };\n}\n\nfunction isPlaceholderString(value: string): boolean {\n\treturn value.trim().startsWith(\"your-\");\n}\n\nfunction listChannelConfigIssues(config: Partial<DingTalkConfig>): string[] {\n\tconst issues: string[] = [];\n\n\tif (!config.clientId) {\n\t\tissues.push(\"Missing required field `clientId`.\");\n\t} else if (isPlaceholderString(config.clientId)) {\n\t\tissues.push(\"Replace placeholder value for `clientId`.\");\n\t}\n\n\tif (!config.clientSecret) {\n\t\tissues.push(\"Missing required field `clientSecret`.\");\n\t} else if (isPlaceholderString(config.clientSecret)) {\n\t\tissues.push(\"Replace placeholder value for `clientSecret`.\");\n\t}\n\n\tif (config.robotCode && isPlaceholderString(config.robotCode)) {\n\t\tissues.push(\"Replace placeholder value for `robotCode`, or set it to an empty string to reuse `clientId`.\");\n\t}\n\n\tif (config.cardTemplateId && isPlaceholderString(config.cardTemplateId)) {\n\t\tissues.push(\n\t\t\t\"Replace placeholder value for `cardTemplateId`, or set it to an empty string to disable AI Card streaming.\",\n\t\t);\n\t}\n\n\tif (Array.isArray(config.allowFrom) && config.allowFrom.some((value) => isPlaceholderString(value))) {\n\t\tissues.push(\"Replace placeholder values in `allowFrom`, or set it to an empty array to allow all users.\");\n\t}\n\n\treturn issues;\n}\n\nexport function printBootstrapSummary(\n\tresult: BootstrapResult,\n\tio: BootstrapIO = console,\n\tpaths: BootstrapPaths = DEFAULT_BOOTSTRAP_PATHS,\n): void {\n\tif (result.created.length === 0) {\n\t\treturn;\n\t}\n\n\tio.log(`Initialized ${paths.appName} under ${paths.appHomeDir}:`);\n\tfor (const item of result.created) {\n\t\tio.log(` - ${item}`);\n\t}\n\tio.log(\"\");\n}\n\nexport function loadConfig(paths: BootstrapPaths = DEFAULT_BOOTSTRAP_PATHS, io: BootstrapIO = console): DingTalkConfig {\n\tlet parsed: DingTalkConfig;\n\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(paths.channelConfigPath, \"utf-8\")) as DingTalkConfig;\n\t} catch (err) {\n\t\tio.error(`Failed to parse configuration: ${paths.channelConfigPath}`);\n\t\tio.error(err instanceof Error ? err.message : String(err));\n\t\tthrow new BootstrapExitError(1);\n\t}\n\n\tconst issues = listChannelConfigIssues(parsed);\n\tif (issues.length > 0) {\n\t\tio.error(`Configuration is not ready: ${paths.channelConfigPath}`);\n\t\tfor (const issue of issues) {\n\t\t\tio.error(` - ${issue}`);\n\t\t}\n\t\tio.error(\"\");\n\t\tio.error(`Fill in ${paths.channelConfigPath} and run \\`${paths.appName}\\` again.`);\n\t\tthrow new BootstrapExitError(1);\n\t}\n\n\tparsed.cardTemplateKey = parsed.cardTemplateKey || \"content\";\n\tparsed.robotCode = parsed.robotCode?.trim() ? parsed.robotCode : parsed.clientId;\n\tif (Array.isArray(parsed.allowFrom)) {\n\t\tparsed.allowFrom = parsed.allowFrom.filter((value) => value.trim().length > 0);\n\t}\n\n\treturn parsed;\n}\n\nexport function parseArgs(\n\targv: string[],\n\tpaths: BootstrapPaths = DEFAULT_BOOTSTRAP_PATHS,\n\tio: BootstrapIO = console,\n): ParsedArgs {\n\tconst args = argv.slice(2);\n\tlet sandbox: SandboxConfig = { type: \"host\" };\n\n\tfor (let index = 0; index < args.length; index += 1) {\n\t\tconst arg = args[index];\n\t\tif (arg.startsWith(\"--sandbox=\")) {\n\t\t\tsandbox = parseSandboxArg(arg.slice(\"--sandbox=\".length));\n\t\t} else if (arg === \"--sandbox\") {\n\t\t\tsandbox = parseSandboxArg(args[index + 1] || \"\");\n\t\t\tindex += 1;\n\t\t} else if (arg === \"--help\" || arg === \"-h\") {\n\t\t\tio.log(`Usage: ${paths.appName} [options]`);\n\t\t\tio.log(\"\");\n\t\t\tio.log(\"Options:\");\n\t\t\tio.log(\" --sandbox=host Run tools on host (default)\");\n\t\t\tio.log(\" --sandbox=docker:<name> Run tools in Docker container\");\n\t\t\tio.log(\"\");\n\t\t\tio.log(`Config: ${paths.channelConfigPath}`);\n\t\t\tio.log(`Workspace: ${paths.workspaceDir}`);\n\t\t\tthrow new BootstrapExitError(0);\n\t\t}\n\t}\n\n\treturn { sandbox };\n}\n\nfunction waitForTasks(tasks: Promise<void>[], timeoutMs: number): Promise<boolean> {\n\tif (tasks.length === 0) {\n\t\treturn Promise.resolve(true);\n\t}\n\n\treturn Promise.race([\n\t\tPromise.allSettled(tasks).then(() => true),\n\t\tnew Promise<boolean>((resolve) => {\n\t\t\tsetTimeout(() => resolve(false), timeoutMs);\n\t\t}),\n\t]);\n}\n\nexport async function bootstrap(argv: string[], options: BootstrapOptions = {}): Promise<AppContext> {\n\tconst env = options.env ?? process.env;\n\tconst io = options.io ?? console;\n\tconst paths = options.paths ?? DEFAULT_BOOTSTRAP_PATHS;\n\tconst registerSignalHandlers = options.registerSignalHandlers ?? true;\n\tconst startServices = options.startServices ?? true;\n\n\tsanitizeProxyEnv(env);\n\n\tconst parsedArgs = parseArgs(argv, paths, io);\n\tconst sandbox = parsedArgs.sandbox;\n\tconst bootstrapResult = bootstrapAppHome(paths);\n\tprintBootstrapSummary(bootstrapResult, io, paths);\n\n\tif (bootstrapResult.channelTemplateCreated) {\n\t\tio.error(`Fill in ${paths.channelConfigPath} and run \\`${paths.appName}\\` again.`);\n\t\tthrow new BootstrapExitError(1);\n\t}\n\n\tconst dingtalkConfig = loadConfig(paths, io);\n\tdingtalkConfig.stateDir = paths.workspaceDir;\n\n\tawait validateSandbox(sandbox);\n\n\tconst store = new ChannelStore({ workingDir: paths.workspaceDir });\n\tconst channelStates = new Map<string, ChannelState>();\n\tconst activeTasks = new Set<Promise<void>>();\n\tlet shuttingDown = false;\n\tlet shutdownPromise: Promise<void> | null = null;\n\n\tconst getState = (channelId: string): ChannelState => {\n\t\tlet state = channelStates.get(channelId);\n\t\tif (!state) {\n\t\t\tconst channelDir = join(paths.workspaceDir, channelId);\n\t\t\tensureChannelMemoryFilesSync(channelDir);\n\t\t\tstate = {\n\t\t\t\trunning: false,\n\t\t\t\trunner: getOrCreateRunner(sandbox, channelId, channelDir),\n\t\t\t\tstopRequested: false,\n\t\t\t};\n\t\t\tchannelStates.set(channelId, state);\n\t\t}\n\t\treturn state;\n\t};\n\n\tconst handler: DingTalkHandler = {\n\t\tisRunning(channelId: string): boolean {\n\t\t\tconst state = channelStates.get(channelId);\n\t\t\treturn state?.running ?? false;\n\t\t},\n\n\t\tasync handleStop(channelId: string, _bot: DingTalkBot): Promise<void> {\n\t\t\tconst state = channelStates.get(channelId);\n\t\t\tif (state?.running) {\n\t\t\t\tstate.stopRequested = true;\n\t\t\t\tvoid state.runner.abort().catch((err) => {\n\t\t\t\t\tlog.logWarning(`[${channelId}] Failed to abort run`, err instanceof Error ? err.message : String(err));\n\t\t\t\t});\n\t\t\t\tlog.logInfo(`[${channelId}] Stop requested`);\n\t\t\t}\n\t\t},\n\n\t\tasync handleBusyMessage(\n\t\t\tevent: DingTalkEvent,\n\t\t\tbot: DingTalkBot,\n\t\t\tmode: BusyMessageMode,\n\t\t\tqueueText: string,\n\t\t): Promise<void> {\n\t\t\tif (shuttingDown) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst state = getState(event.channelId);\n\t\t\tconst trimmedQueueText = queueText.trim();\n\n\t\t\tawait store.logMessage(event.channelId, {\n\t\t\t\tdate: new Date().toISOString(),\n\t\t\t\tts: event.ts,\n\t\t\t\tuser: event.user,\n\t\t\t\tuserName: event.userName,\n\t\t\t\ttext: event.text,\n\t\t\t\tisBot: false,\n\t\t\t\tdeliveryMode: mode,\n\t\t\t\tskipContextSync: true,\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tif (mode === \"followUp\") {\n\t\t\t\t\tawait state.runner.queueFollowUp(trimmedQueueText, event.userName);\n\t\t\t\t} else {\n\t\t\t\t\tawait state.runner.queueSteer(trimmedQueueText, event.userName);\n\t\t\t\t}\n\n\t\t\t\tconst confirmation =\n\t\t\t\t\tmode === \"followUp\"\n\t\t\t\t\t\t? \"Queued as follow-up. I’ll handle it after the current task completes.\"\n\t\t\t\t\t\t: event.text.trim().startsWith(\"/\")\n\t\t\t\t\t\t\t? \"Queued as steer. I’ll apply it after the current tool step finishes.\"\n\t\t\t\t\t\t\t: \"Queued as steer. I’ll apply this after the current tool step finishes. Use `/followup <message>` to queue it after completion.\";\n\t\t\t\tawait bot.sendPlain(event.channelId, confirmation);\n\t\t\t\tlog.logInfo(`[${event.channelId}] Queued ${mode}: ${trimmedQueueText.substring(0, 80)}`);\n\t\t\t} catch (err) {\n\t\t\t\tconst errMsg = err instanceof Error ? err.message : String(err);\n\t\t\t\tlog.logWarning(`[${event.channelId}] Failed to queue ${mode}`, errMsg);\n\t\t\t\tawait bot.sendPlain(event.channelId, `Could not queue this message: ${errMsg}`);\n\t\t\t}\n\t\t},\n\n\t\tasync handleEvent(event: DingTalkEvent, bot: DingTalkBot, _isEvent?: boolean): Promise<void> {\n\t\t\tif (shuttingDown) {\n\t\t\t\tlog.logInfo(`[${event.channelId}] Ignoring event during shutdown`);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst state = getState(event.channelId);\n\t\t\tconst task = (async () => {\n\t\t\t\tstate.running = true;\n\t\t\t\tstate.stopRequested = false;\n\n\t\t\t\tawait store.logMessage(event.channelId, {\n\t\t\t\t\tdate: new Date().toISOString(),\n\t\t\t\t\tts: event.ts,\n\t\t\t\t\tuser: event.user,\n\t\t\t\t\tuserName: event.userName,\n\t\t\t\t\ttext: event.text,\n\t\t\t\t\tisBot: false,\n\t\t\t\t});\n\n\t\t\t\ttry {\n\t\t\t\t\tconst ctx = createDingTalkContext(event, bot, store);\n\t\t\t\t\tconst builtInCommand = parseBuiltInCommand(event.text);\n\n\t\t\t\t\tif (builtInCommand) {\n\t\t\t\t\t\tlog.logInfo(`[${event.channelId}] Executing command: ${builtInCommand.rawText}`);\n\t\t\t\t\t\tawait state.runner.handleBuiltinCommand(ctx, builtInCommand);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tlog.logInfo(`[${event.channelId}] Starting run: ${event.text.substring(0, 50)}`);\n\t\t\t\t\tconst result = await state.runner.run(ctx, store);\n\n\t\t\t\t\tif (result.stopReason === \"aborted\" && state.stopRequested) {\n\t\t\t\t\t\tlog.logInfo(`[${event.channelId}] Stopped`);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlog.logWarning(`[${event.channelId}] Run error`, err instanceof Error ? err.message : String(err));\n\t\t\t\t} finally {\n\t\t\t\t\tstate.running = false;\n\t\t\t\t}\n\t\t\t})();\n\n\t\t\tactiveTasks.add(task);\n\t\t\ttry {\n\t\t\t\tawait task;\n\t\t\t} finally {\n\t\t\t\tactiveTasks.delete(task);\n\t\t\t}\n\t\t},\n\t};\n\n\tlog.logStartup(paths.workspaceDir, sandbox.type === \"host\" ? \"host\" : `docker:${sandbox.container}`);\n\n\tconst bot = new DingTalkBot(handler, dingtalkConfig);\n\tconst eventsWatcher = createEventsWatcher(paths.workspaceDir, bot);\n\n\tconst shutdownWithReason = async (reason: NodeJS.Signals | \"manual\"): Promise<void> => {\n\t\tif (shutdownPromise) {\n\t\t\treturn shutdownPromise;\n\t\t}\n\n\t\tshutdownPromise = (async () => {\n\t\t\tshuttingDown = true;\n\t\t\tlog.logInfo(`Shutting down (${reason})...`);\n\n\t\t\teventsWatcher.stop();\n\t\t\tawait bot.stop();\n\n\t\t\tconst runningTasks = Array.from(activeTasks);\n\t\t\tif (runningTasks.length > 0) {\n\t\t\t\tlog.logInfo(`Waiting for ${runningTasks.length} active task(s) to finish`);\n\t\t\t\tconst completed = await waitForTasks(runningTasks, SHUTDOWN_WAIT_MS);\n\n\t\t\t\tif (!completed) {\n\t\t\t\t\tlog.logWarning(`Shutdown grace period exceeded ${SHUTDOWN_WAIT_MS}ms, aborting active runs`);\n\t\t\t\t\tconst aborts: Promise<void>[] = [];\n\t\t\t\t\tfor (const [channelId, state] of channelStates) {\n\t\t\t\t\t\tif (!state.running) continue;\n\t\t\t\t\t\tstate.stopRequested = true;\n\t\t\t\t\t\tlog.logInfo(`[${channelId}] Aborting active run for shutdown`);\n\t\t\t\t\t\taborts.push(\n\t\t\t\t\t\t\tstate.runner.abort().catch((err) => {\n\t\t\t\t\t\t\t\tlog.logWarning(\n\t\t\t\t\t\t\t\t\t`[${channelId}] Failed to abort run during shutdown`,\n\t\t\t\t\t\t\t\t\terr instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tawait Promise.allSettled(aborts);\n\n\t\t\t\t\tconst remainingTasks = Array.from(activeTasks);\n\t\t\t\t\tif (remainingTasks.length > 0) {\n\t\t\t\t\t\tconst abortedCompleted = await waitForTasks(remainingTasks, SHUTDOWN_ABORT_WAIT_MS);\n\t\t\t\t\t\tif (!abortedCompleted) {\n\t\t\t\t\t\t\tlog.logWarning(`Shutdown forced exit with ${remainingTasks.length} task(s) still active`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\n\t\treturn shutdownPromise;\n\t};\n\n\tif (registerSignalHandlers) {\n\t\tprocess.once(\"SIGINT\", () => {\n\t\t\tvoid shutdownWithReason(\"SIGINT\").finally(() => {\n\t\t\t\tprocess.exit(0);\n\t\t\t});\n\t\t});\n\n\t\tprocess.once(\"SIGTERM\", () => {\n\t\t\tvoid shutdownWithReason(\"SIGTERM\").finally(() => {\n\t\t\t\tprocess.exit(0);\n\t\t\t});\n\t\t});\n\t}\n\n\tif (startServices) {\n\t\teventsWatcher.start();\n\t\tvoid bot.start();\n\t}\n\n\treturn {\n\t\tbot,\n\t\tstore,\n\t\tshutdown: async () => {\n\t\t\tawait shutdownWithReason(\"manual\");\n\t\t},\n\t};\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../../src/runtime/delivery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAiP/C,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,GAAG,eAAe,CAElH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../../src/runtime/delivery.ts"],"names":[],"mappings":"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","sourcesContent":["import * as log from \"../log.js\";\nimport type { DingTalkBot, DingTalkContext, DingTalkEvent } from \"./dingtalk.js\";\nimport type { ChannelStore } from \"./store.js\";\n\nconst MIN_UPDATE_INTERVAL_MS = 800;\n\ntype DeliveryMode = \"progress\" | \"finalize-existing\" | \"finalize-with-fallback\" | \"silent\";\n\nclass ChannelDeliveryController {\n\tprivate progressText = \"\";\n\tprivate mode: DeliveryMode = \"progress\";\n\tprivate desiredRevision = 0;\n\tprivate appliedRevision = 0;\n\tprivate running = false;\n\tprivate closed = false;\n\tprivate finalResponseDelivered = false;\n\tprivate progressWindowStartedAt = 0;\n\tprivate lastDeliveredAt = 0;\n\tprivate timer: NodeJS.Timeout | null = null;\n\tprivate flushWaiters: Array<() => void> = [];\n\n\tconstructor(\n\t\tprivate event: DingTalkEvent,\n\t\tprivate bot: DingTalkBot,\n\t\tprivate store: ChannelStore,\n\t) {}\n\n\tbuildContext(): DingTalkContext {\n\t\treturn {\n\t\t\tmessage: {\n\t\t\t\ttext: this.event.text,\n\t\t\t\trawText: this.event.text,\n\t\t\t\tuser: this.event.user,\n\t\t\t\tuserName: this.event.userName,\n\t\t\t\tchannel: this.event.channelId,\n\t\t\t\tts: this.event.ts,\n\t\t\t},\n\t\t\tchannelName: this.event.channelId,\n\t\t\trespond: async (text: string, shouldLog = true) => this.appendProgress(text, shouldLog),\n\t\t\trespondPlain: async (text: string, shouldLog = true) => this.sendFinal(text, shouldLog),\n\t\t\treplaceMessage: async (text: string) => this.replaceWithFinal(text),\n\t\t\trespondInThread: async (text: string) => {\n\t\t\t\tlog.logInfo(`[thread] ${text.substring(0, 200)}`);\n\t\t\t},\n\t\t\tsetTyping: async (_isTyping: boolean) => {},\n\t\t\tsetWorking: async (_working: boolean) => {},\n\t\t\tdeleteMessage: async () => this.silence(),\n\t\t\tflush: async () => this.flush(),\n\t\t\tclose: async () => this.close(),\n\t\t};\n\t}\n\n\tprivate async appendProgress(text: string, shouldLog: boolean): Promise<void> {\n\t\tif (this.closed || this.finalResponseDelivered || !text.trim()) return;\n\n\t\tthis.progressText = this.progressText ? `${this.progressText}\\n\\n${text}` : text;\n\t\tif (this.progressWindowStartedAt === 0) {\n\t\t\tthis.progressWindowStartedAt = Date.now();\n\t\t}\n\t\tif (shouldLog) {\n\t\t\tawait this.store.logBotResponse(this.event.channelId, text, Date.now().toString());\n\t\t}\n\n\t\tthis.mode = \"progress\";\n\t\tthis.bumpRevision(false);\n\t}\n\n\tprivate async sendFinal(text: string, shouldLog: boolean): Promise<boolean> {\n\t\tif (this.closed || this.finalResponseDelivered) return this.finalResponseDelivered;\n\n\t\tif (shouldLog) {\n\t\t\tawait this.store.logBotResponse(this.event.channelId, text, Date.now().toString());\n\t\t}\n\n\t\tconst delivered = await this.bot.sendPlain(this.event.channelId, text);\n\t\tif (!delivered) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.finalResponseDelivered = true;\n\t\tthis.mode = \"finalize-existing\";\n\t\tthis.bumpRevision(true);\n\t\treturn true;\n\t}\n\n\tprivate async replaceWithFinal(text: string): Promise<void> {\n\t\tif (this.closed || this.finalResponseDelivered) return;\n\n\t\tthis.progressText = text;\n\t\tthis.mode = \"finalize-with-fallback\";\n\t\tthis.bumpRevision(true);\n\t}\n\n\tprivate async silence(): Promise<void> {\n\t\tif (this.closed) return;\n\n\t\tthis.finalResponseDelivered = true;\n\t\tthis.mode = \"silent\";\n\t\tthis.bumpRevision(true);\n\t}\n\n\tprivate bumpRevision(forceImmediate: boolean): void {\n\t\tthis.desiredRevision++;\n\t\tthis.schedule(forceImmediate);\n\t}\n\n\tprivate schedule(forceImmediate: boolean): void {\n\t\tif (this.running) return;\n\n\t\tif (this.timer) {\n\t\t\tclearTimeout(this.timer);\n\t\t\tthis.timer = null;\n\t\t}\n\n\t\tconst delay =\n\t\t\tforceImmediate || this.mode !== \"progress\"\n\t\t\t\t? 0\n\t\t\t\t: Math.max(\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tMIN_UPDATE_INTERVAL_MS -\n\t\t\t\t\t\t\t(Date.now() - (this.lastDeliveredAt > 0 ? this.lastDeliveredAt : this.progressWindowStartedAt)),\n\t\t\t\t\t);\n\n\t\tif (delay === 0) {\n\t\t\tvoid this.runSyncLoop();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.timer = setTimeout(() => {\n\t\t\tthis.timer = null;\n\t\t\tvoid this.runSyncLoop();\n\t\t}, delay);\n\t}\n\n\tprivate async runSyncLoop(): Promise<void> {\n\t\tif (this.running) return;\n\t\tthis.running = true;\n\n\t\ttry {\n\t\t\twhile (this.appliedRevision < this.desiredRevision) {\n\t\t\t\tconst mode = this.mode;\n\t\t\t\tconst throttleBaseAt = this.lastDeliveredAt > 0 ? this.lastDeliveredAt : this.progressWindowStartedAt;\n\t\t\t\tif (mode === \"progress\" && throttleBaseAt > 0) {\n\t\t\t\t\tconst remaining = MIN_UPDATE_INTERVAL_MS - (Date.now() - throttleBaseAt);\n\t\t\t\t\tif (remaining > 0) {\n\t\t\t\t\t\tthis.timer = setTimeout(() => {\n\t\t\t\t\t\t\tthis.timer = null;\n\t\t\t\t\t\t\tvoid this.runSyncLoop();\n\t\t\t\t\t\t}, remaining);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst revision = this.desiredRevision;\n\t\t\t\tconst content = this.progressText.trim();\n\t\t\t\tlet touchedRemote = false;\n\n\t\t\t\ttry {\n\t\t\t\t\tif (mode === \"progress\") {\n\t\t\t\t\t\tif (content) {\n\t\t\t\t\t\t\ttouchedRemote = await this.bot.streamToCard(this.event.channelId, this.progressText);\n\t\t\t\t\t\t\tif (!touchedRemote) {\n\t\t\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (mode === \"finalize-existing\") {\n\t\t\t\t\t\tif (content) {\n\t\t\t\t\t\t\ttouchedRemote = await this.bot.finalizeExistingCard(this.event.channelId, this.progressText);\n\t\t\t\t\t\t\tif (!touchedRemote) {\n\t\t\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (mode === \"finalize-with-fallback\") {\n\t\t\t\t\t\tif (content) {\n\t\t\t\t\t\t\ttouchedRemote = await this.bot.finalizeCard(this.event.channelId, this.progressText);\n\t\t\t\t\t\t\tif (!touchedRemote) {\n\t\t\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (mode === \"silent\") {\n\t\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tlog.logWarning(\n\t\t\t\t\t\t`[${this.event.channelId}] Delivery sync failed`,\n\t\t\t\t\t\terr instanceof Error ? err.message : String(err),\n\t\t\t\t\t);\n\t\t\t\t\tthis.bot.discardCard(this.event.channelId);\n\t\t\t\t}\n\n\t\t\t\tif (touchedRemote) {\n\t\t\t\t\tthis.lastDeliveredAt = Date.now();\n\t\t\t\t}\n\t\t\t\tif (mode !== \"progress\" || touchedRemote) {\n\t\t\t\t\tthis.progressWindowStartedAt = 0;\n\t\t\t\t}\n\t\t\t\tthis.appliedRevision = revision;\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.running = false;\n\t\t\tthis.resolveFlushWaiters();\n\n\t\t\tif (this.appliedRevision < this.desiredRevision && !this.timer) {\n\t\t\t\tthis.schedule(false);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate isSettled(): boolean {\n\t\treturn !this.running && !this.timer && this.appliedRevision >= this.desiredRevision;\n\t}\n\n\tprivate resolveFlushWaiters(): void {\n\t\tif (!this.isSettled()) return;\n\t\tconst waiters = this.flushWaiters;\n\t\tthis.flushWaiters = [];\n\t\tfor (const resolve of waiters) {\n\t\t\tresolve();\n\t\t}\n\t}\n\n\tprivate async flush(): Promise<void> {\n\t\tif (this.isSettled()) return;\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tthis.flushWaiters.push(resolve);\n\t\t});\n\t}\n\n\tprivate async close(): Promise<void> {\n\t\tif (this.closed) {\n\t\t\tawait this.flush();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.closed = true;\n\t\tawait this.flush();\n\t}\n}\n\nexport function createDingTalkContext(event: DingTalkEvent, bot: DingTalkBot, store: ChannelStore): DingTalkContext {\n\treturn new ChannelDeliveryController(event, bot, store).buildContext();\n}\n"]}
|
|
@@ -57,6 +57,7 @@ export declare class DingTalkBot {
|
|
|
57
57
|
private lastSocketAvailableTime;
|
|
58
58
|
private activeMessageProcessing;
|
|
59
59
|
private keepAliveTimer;
|
|
60
|
+
private reconnectTimer;
|
|
60
61
|
private isReconnecting;
|
|
61
62
|
private isStopped;
|
|
62
63
|
private reconnectAttempts;
|
|
@@ -68,6 +69,15 @@ export declare class DingTalkBot {
|
|
|
68
69
|
* Maintains a FIFO buffer of at most 200 entries.
|
|
69
70
|
*/
|
|
70
71
|
private markProcessed;
|
|
72
|
+
private getSocket;
|
|
73
|
+
private isSocketLike;
|
|
74
|
+
private setTrackedTimeout;
|
|
75
|
+
private setTrackedInterval;
|
|
76
|
+
private clearKeepAliveTimer;
|
|
77
|
+
private clearReconnectTimer;
|
|
78
|
+
private clearAllTimers;
|
|
79
|
+
private waitForDelay;
|
|
80
|
+
private scheduleReconnect;
|
|
71
81
|
start(): Promise<void>;
|
|
72
82
|
private handleRawMessage;
|
|
73
83
|
private doReconnect;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dingtalk.d.ts","sourceRoot":"","sources":["../../src/runtime/dingtalk.ts"],"names":[],"mappings":"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"}
|