clawchef 0.1.11 → 0.1.12
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 +1 -0
- package/dist/openclaw/command-provider.js +21 -0
- package/dist/orchestrator.js +13 -0
- package/dist/recipe.js +11 -0
- package/dist/schema.js +2 -2
- package/package.json +1 -1
- package/src/openclaw/command-provider.ts +25 -0
- package/src/orchestrator.ts +18 -1
- package/src/recipe.ts +14 -1
- package/src/schema.ts +2 -2
package/README.md
CHANGED
|
@@ -420,6 +420,7 @@ Supported common fields:
|
|
|
420
420
|
`channels[].agent` currently supports `channel: "telegram"` only.
|
|
421
421
|
If `agent` is set and `account` is omitted, clawchef defaults `account` to the same value as `agent`.
|
|
422
422
|
`channels[].group_policy` currently supports `channel: "telegram"` only and is applied after `channels add` via `openclaw config set` so it is not overwritten by add-flow writes.
|
|
423
|
+
If `channel: "telegram"` has `token: ""` or `bot_token: ""`, clawchef auto-disables that telegram account (`enabled=false`) and skips channel add/bind.
|
|
423
424
|
|
|
424
425
|
## Workspace path behavior
|
|
425
426
|
|
|
@@ -136,6 +136,21 @@ function telegramGroupPolicyPath(account) {
|
|
|
136
136
|
}
|
|
137
137
|
return `channels.telegram.accounts[${trimmed}].groupPolicy`;
|
|
138
138
|
}
|
|
139
|
+
function telegramEnabledPath(account) {
|
|
140
|
+
const trimmed = account?.trim();
|
|
141
|
+
if (!trimmed) {
|
|
142
|
+
return "channels.telegram.enabled";
|
|
143
|
+
}
|
|
144
|
+
return `channels.telegram.accounts[${trimmed}].enabled`;
|
|
145
|
+
}
|
|
146
|
+
function shouldAutoDisableTelegramChannel(channel) {
|
|
147
|
+
if (channel.channel !== "telegram") {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
const emptyToken = channel.token !== undefined && channel.token.trim().length === 0;
|
|
151
|
+
const emptyBotToken = channel.bot_token !== undefined && channel.bot_token.trim().length === 0;
|
|
152
|
+
return emptyToken || emptyBotToken;
|
|
153
|
+
}
|
|
139
154
|
async function chooseVersionMismatchAction(currentVersion, expectedVersion, silent) {
|
|
140
155
|
if (silent) {
|
|
141
156
|
return "force";
|
|
@@ -403,6 +418,12 @@ export class CommandOpenClawProvider {
|
|
|
403
418
|
}
|
|
404
419
|
async configureChannel(config, channel, dryRun) {
|
|
405
420
|
const bin = config.bin ?? "openclaw";
|
|
421
|
+
if (shouldAutoDisableTelegramChannel(channel)) {
|
|
422
|
+
const enabledPath = telegramEnabledPath(channel.account);
|
|
423
|
+
const disableCmd = `${bin} config set ${shellQuote(enabledPath)} false --strict-json`;
|
|
424
|
+
await runShell(disableCmd, dryRun);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
406
427
|
const enablePluginTemplate = config.commands?.enable_plugin;
|
|
407
428
|
if (enablePluginTemplate?.trim()) {
|
|
408
429
|
const enablePluginCmd = fillTemplate(enablePluginTemplate, {
|
package/dist/orchestrator.js
CHANGED
|
@@ -76,6 +76,14 @@ function isHttpUrl(value) {
|
|
|
76
76
|
return false;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
+
function shouldAutoDisableTelegramChannel(channel) {
|
|
80
|
+
if (channel.channel !== "telegram") {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const emptyToken = channel.token !== undefined && channel.token.trim().length === 0;
|
|
84
|
+
const emptyBotToken = channel.bot_token !== undefined && channel.bot_token.trim().length === 0;
|
|
85
|
+
return emptyToken || emptyBotToken;
|
|
86
|
+
}
|
|
79
87
|
function isNotFoundError(err) {
|
|
80
88
|
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
81
89
|
}
|
|
@@ -314,7 +322,12 @@ export async function runRecipe(recipe, recipeOrigin, options, logger) {
|
|
|
314
322
|
const effectiveChannel = channel.agent?.trim() && !channel.account?.trim()
|
|
315
323
|
? { ...channel, account: channel.agent.trim() }
|
|
316
324
|
: channel;
|
|
325
|
+
const autoDisabledTelegram = shouldAutoDisableTelegramChannel(effectiveChannel);
|
|
317
326
|
await provider.configureChannel(recipe.openclaw, effectiveChannel, options.dryRun);
|
|
327
|
+
if (autoDisabledTelegram) {
|
|
328
|
+
logger.info(`Telegram channel disabled due to empty bot token: ${effectiveChannel.channel}${effectiveChannel.account ? `/${effectiveChannel.account}` : ""}`);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
318
331
|
logger.info(`Channel configured: ${effectiveChannel.channel}${effectiveChannel.account ? `/${effectiveChannel.account}` : ""}`);
|
|
319
332
|
if (effectiveChannel.agent?.trim()) {
|
|
320
333
|
await provider.bindChannelAgent(recipe.openclaw, effectiveChannel, effectiveChannel.agent, options.dryRun);
|
package/dist/recipe.js
CHANGED
|
@@ -44,6 +44,14 @@ const ALLOWED_CHANNELS = new Set([
|
|
|
44
44
|
]);
|
|
45
45
|
const CHANNEL_SECRET_FIELDS = ["token", "bot_token", "access_token", "app_token", "password"];
|
|
46
46
|
const TEMPLATE_TOKEN_RE = /\$\{[A-Za-z_][A-Za-z0-9_]*\}/;
|
|
47
|
+
function hasExplicitEmptyTelegramToken(channel) {
|
|
48
|
+
if (channel.channel !== "telegram") {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const emptyToken = channel.token !== undefined && channel.token.trim().length === 0;
|
|
52
|
+
const emptyBotToken = channel.bot_token !== undefined && channel.bot_token.trim().length === 0;
|
|
53
|
+
return emptyToken || emptyBotToken;
|
|
54
|
+
}
|
|
47
55
|
function assertNoInlineSecrets(recipe) {
|
|
48
56
|
const bootstrap = recipe.openclaw.bootstrap;
|
|
49
57
|
if (bootstrap) {
|
|
@@ -222,6 +230,9 @@ function semanticValidate(recipe) {
|
|
|
222
230
|
}
|
|
223
231
|
return /(token|password|secret|api[_-]?key|webhook)/i.test(key) && String(value).trim().length > 0;
|
|
224
232
|
});
|
|
233
|
+
if (hasExplicitEmptyTelegramToken(channel)) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
225
236
|
if (!hasAuth) {
|
|
226
237
|
throw new ClawChefError(`channels[] entry for ${channel.channel} requires at least one auth input (for example token/bot_token/access_token/token_file/use_env)`);
|
|
227
238
|
}
|
package/dist/schema.js
CHANGED
|
@@ -107,10 +107,10 @@ const channelSchema = z
|
|
|
107
107
|
login_mode: z.enum(["interactive"]).optional(),
|
|
108
108
|
login_account: z.string().min(1).optional(),
|
|
109
109
|
name: z.string().min(1).optional(),
|
|
110
|
-
token: z.string().
|
|
110
|
+
token: z.string().optional(),
|
|
111
111
|
token_file: z.string().min(1).optional(),
|
|
112
112
|
use_env: z.boolean().optional(),
|
|
113
|
-
bot_token: z.string().
|
|
113
|
+
bot_token: z.string().optional(),
|
|
114
114
|
access_token: z.string().min(1).optional(),
|
|
115
115
|
app_token: z.string().min(1).optional(),
|
|
116
116
|
webhook_url: z.string().min(1).optional(),
|
package/package.json
CHANGED
|
@@ -186,6 +186,23 @@ function telegramGroupPolicyPath(account: string | undefined): string {
|
|
|
186
186
|
return `channels.telegram.accounts[${trimmed}].groupPolicy`;
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
function telegramEnabledPath(account: string | undefined): string {
|
|
190
|
+
const trimmed = account?.trim();
|
|
191
|
+
if (!trimmed) {
|
|
192
|
+
return "channels.telegram.enabled";
|
|
193
|
+
}
|
|
194
|
+
return `channels.telegram.accounts[${trimmed}].enabled`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function shouldAutoDisableTelegramChannel(channel: ChannelDef): boolean {
|
|
198
|
+
if (channel.channel !== "telegram") {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
const emptyToken = channel.token !== undefined && channel.token.trim().length === 0;
|
|
202
|
+
const emptyBotToken = channel.bot_token !== undefined && channel.bot_token.trim().length === 0;
|
|
203
|
+
return emptyToken || emptyBotToken;
|
|
204
|
+
}
|
|
205
|
+
|
|
189
206
|
type VersionMismatchChoice = "ignore" | "abort" | "force";
|
|
190
207
|
|
|
191
208
|
async function chooseVersionMismatchAction(
|
|
@@ -510,6 +527,14 @@ export class CommandOpenClawProvider implements OpenClawProvider {
|
|
|
510
527
|
|
|
511
528
|
async configureChannel(config: OpenClawSection, channel: ChannelDef, dryRun: boolean): Promise<void> {
|
|
512
529
|
const bin = config.bin ?? "openclaw";
|
|
530
|
+
|
|
531
|
+
if (shouldAutoDisableTelegramChannel(channel)) {
|
|
532
|
+
const enabledPath = telegramEnabledPath(channel.account);
|
|
533
|
+
const disableCmd = `${bin} config set ${shellQuote(enabledPath)} false --strict-json`;
|
|
534
|
+
await runShell(disableCmd, dryRun);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
513
538
|
const enablePluginTemplate = config.commands?.enable_plugin;
|
|
514
539
|
if (enablePluginTemplate?.trim()) {
|
|
515
540
|
const enablePluginCmd = fillTemplate(enablePluginTemplate, {
|
package/src/orchestrator.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { validateReply } from "./assertions.js";
|
|
|
8
8
|
import { ClawChefError } from "./errors.js";
|
|
9
9
|
import { Logger } from "./logger.js";
|
|
10
10
|
import { createProvider } from "./openclaw/factory.js";
|
|
11
|
-
import type { Recipe, RunOptions } from "./types.js";
|
|
11
|
+
import type { ChannelDef, Recipe, RunOptions } from "./types.js";
|
|
12
12
|
import type { RecipeOrigin } from "./recipe.js";
|
|
13
13
|
|
|
14
14
|
async function exists(filePath: string): Promise<boolean> {
|
|
@@ -84,6 +84,15 @@ function isHttpUrl(value: string): boolean {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function shouldAutoDisableTelegramChannel(channel: ChannelDef): boolean {
|
|
88
|
+
if (channel.channel !== "telegram") {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
const emptyToken = channel.token !== undefined && channel.token.trim().length === 0;
|
|
92
|
+
const emptyBotToken = channel.bot_token !== undefined && channel.bot_token.trim().length === 0;
|
|
93
|
+
return emptyToken || emptyBotToken;
|
|
94
|
+
}
|
|
95
|
+
|
|
87
96
|
function isNotFoundError(err: unknown): boolean {
|
|
88
97
|
return typeof err === "object" && err !== null && "code" in err && (err as { code?: string }).code === "ENOENT";
|
|
89
98
|
}
|
|
@@ -362,8 +371,16 @@ export async function runRecipe(
|
|
|
362
371
|
const effectiveChannel = channel.agent?.trim() && !channel.account?.trim()
|
|
363
372
|
? { ...channel, account: channel.agent.trim() }
|
|
364
373
|
: channel;
|
|
374
|
+
const autoDisabledTelegram = shouldAutoDisableTelegramChannel(effectiveChannel);
|
|
365
375
|
|
|
366
376
|
await provider.configureChannel(recipe.openclaw, effectiveChannel, options.dryRun);
|
|
377
|
+
if (autoDisabledTelegram) {
|
|
378
|
+
logger.info(
|
|
379
|
+
`Telegram channel disabled due to empty bot token: ${effectiveChannel.channel}${effectiveChannel.account ? `/${effectiveChannel.account}` : ""}`,
|
|
380
|
+
);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
|
|
367
384
|
logger.info(`Channel configured: ${effectiveChannel.channel}${effectiveChannel.account ? `/${effectiveChannel.account}` : ""}`);
|
|
368
385
|
if (effectiveChannel.agent?.trim()) {
|
|
369
386
|
await provider.bindChannelAgent(recipe.openclaw, effectiveChannel, effectiveChannel.agent, options.dryRun);
|
package/src/recipe.ts
CHANGED
|
@@ -7,7 +7,7 @@ import YAML from "js-yaml";
|
|
|
7
7
|
import { recipeSchema } from "./schema.js";
|
|
8
8
|
import { ClawChefError } from "./errors.js";
|
|
9
9
|
import { deepResolveTemplates } from "./template.js";
|
|
10
|
-
import type { Recipe, RunOptions } from "./types.js";
|
|
10
|
+
import type { ChannelDef, Recipe, RunOptions } from "./types.js";
|
|
11
11
|
|
|
12
12
|
export type RecipeOrigin =
|
|
13
13
|
| {
|
|
@@ -73,6 +73,15 @@ const CHANNEL_SECRET_FIELDS = ["token", "bot_token", "access_token", "app_token"
|
|
|
73
73
|
|
|
74
74
|
const TEMPLATE_TOKEN_RE = /\$\{[A-Za-z_][A-Za-z0-9_]*\}/;
|
|
75
75
|
|
|
76
|
+
function hasExplicitEmptyTelegramToken(channel: ChannelDef): boolean {
|
|
77
|
+
if (channel.channel !== "telegram") {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const emptyToken = channel.token !== undefined && channel.token.trim().length === 0;
|
|
81
|
+
const emptyBotToken = channel.bot_token !== undefined && channel.bot_token.trim().length === 0;
|
|
82
|
+
return emptyToken || emptyBotToken;
|
|
83
|
+
}
|
|
84
|
+
|
|
76
85
|
function assertNoInlineSecrets(recipe: Recipe): void {
|
|
77
86
|
const bootstrap = recipe.openclaw.bootstrap;
|
|
78
87
|
if (bootstrap) {
|
|
@@ -291,6 +300,10 @@ function semanticValidate(recipe: Recipe): void {
|
|
|
291
300
|
return /(token|password|secret|api[_-]?key|webhook)/i.test(key) && String(value).trim().length > 0;
|
|
292
301
|
});
|
|
293
302
|
|
|
303
|
+
if (hasExplicitEmptyTelegramToken(channel)) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
|
|
294
307
|
if (!hasAuth) {
|
|
295
308
|
throw new ClawChefError(
|
|
296
309
|
`channels[] entry for ${channel.channel} requires at least one auth input (for example token/bot_token/access_token/token_file/use_env)`,
|
package/src/schema.ts
CHANGED
|
@@ -117,10 +117,10 @@ const channelSchema = z
|
|
|
117
117
|
login_mode: z.enum(["interactive"]).optional(),
|
|
118
118
|
login_account: z.string().min(1).optional(),
|
|
119
119
|
name: z.string().min(1).optional(),
|
|
120
|
-
token: z.string().
|
|
120
|
+
token: z.string().optional(),
|
|
121
121
|
token_file: z.string().min(1).optional(),
|
|
122
122
|
use_env: z.boolean().optional(),
|
|
123
|
-
bot_token: z.string().
|
|
123
|
+
bot_token: z.string().optional(),
|
|
124
124
|
access_token: z.string().min(1).optional(),
|
|
125
125
|
app_token: z.string().min(1).optional(),
|
|
126
126
|
webhook_url: z.string().min(1).optional(),
|