@assuredloop/orca-bot-kit 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -4
- package/SETUP.md +16 -15
- package/package.json +2 -1
- package/rules/limits.md +3 -3
- package/skills/obk-bot-building/SKILL.md +56 -23
- package/src/book.js +70 -9
- package/src/bot.js +25 -9
- package/src/cli.js +112 -32
- package/src/conversations.js +24 -0
- package/src/message.js +83 -11
- package/src/orca.js +6 -6
- package/src/permissions.js +430 -16
- package/src/up.js +50 -16
- package/src/usage.js +79 -39
package/README.md
CHANGED
|
@@ -543,6 +543,14 @@ runs `obk session mailbox` before the harness. For the same reason a session's
|
|
|
543
543
|
mail is read in its own tab. `message check` from any other tab reads nothing,
|
|
544
544
|
binds nothing, and says so; the mail waits.
|
|
545
545
|
|
|
546
|
+
For one session, that step, a `message check`, and `up` writing a new tab for it
|
|
547
|
+
into the book take turns. So two starts of the same session at once leave its
|
|
548
|
+
mailbox bound to the tab the book names, and no mail is read in the other one.
|
|
549
|
+
Each waits a minute at most for its turn. A step that waits it out leaves the
|
|
550
|
+
mailbox where it was and says so, and the harness starts all the same. A check
|
|
551
|
+
holds its turn for 40 seconds at most: with more mail than it reads in that
|
|
552
|
+
time, it shows what it read and says the rest is still waiting.
|
|
553
|
+
|
|
546
554
|
Nothing in a mailbox wakes anybody, so `message send` also types one line into
|
|
547
555
|
the receiver's tab telling it to look. Both harnesses take a typed line as the
|
|
548
556
|
next thing to do rather than cutting into what they are doing, which is the
|
|
@@ -579,10 +587,11 @@ The tests come in two layers, and every test file belongs to one of them.
|
|
|
579
587
|
|
|
580
588
|
`npm test` is `test/*.test.js`: unit tests and end-to-end runs of the CLI in a
|
|
581
589
|
throwaway folder, with a fake `orca` on PATH. It needs nothing but Node, so
|
|
582
|
-
[GitHub Actions](.github/workflows/ci.yml) runs it on every pull request
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
installed
|
|
590
|
+
[GitHub Actions](.github/workflows/ci.yml) runs it on every pull request, split
|
|
591
|
+
into shards that run side by side, on Node 25.8.0, the current line. A release
|
|
592
|
+
runs every shard on 25.8.0 and again on 24.21.0, the floor `engines.node`
|
|
593
|
+
promises users, with only the kit's own dependencies installed, before anything
|
|
594
|
+
is published.
|
|
586
595
|
|
|
587
596
|
`npm run test:system` is `test/system/*.test.js`: the real `obk` against the
|
|
588
597
|
real Orca and the real harnesses on your own machine. No CI runner can do that,
|
package/SETUP.md
CHANGED
|
@@ -191,19 +191,21 @@ What you will see, and the usual answer. Each goes as one
|
|
|
191
191
|
text and **no** `--enter`, because a menu takes a return as the keypress it is
|
|
192
192
|
waiting for:
|
|
193
193
|
|
|
194
|
-
| On screen | Send | Which is |
|
|
195
|
-
|
|
196
|
-
| Claude Code's folder trust list | `\x1b[B\r` | down, then return: its selection starts on **No, exit** |
|
|
197
|
-
| Codex's directory trust, `1. Trust and continue` (older: `1. Yes, continue`) |
|
|
198
|
-
| Codex's `Hooks need review` |
|
|
199
|
-
| Codex's `/new`: `Where should the new conversation run?` |
|
|
200
|
-
| Codex's update offer, `1. Update now` |
|
|
201
|
-
| `[oh-my-zsh] Would you like to update?` | `n` | they update their own shell |
|
|
202
|
-
| Claude Code's `Teach auto mode about your environment?` | `
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
194
|
+
| On screen | Send | Which is | Proven on |
|
|
195
|
+
|---|---|---|---|
|
|
196
|
+
| Claude Code's folder trust list | `\x1b[B\r` | down, then return: its selection starts on **No, exit** | Claude Code 2.1.283 |
|
|
197
|
+
| Codex's directory trust, `1. Trust and continue` (older: `1. Yes, continue`) | `\r` | return: its selection starts on **Trust and continue** | Codex 0.157.1 |
|
|
198
|
+
| Codex's `Hooks need review` | `\x1b[B\r` | down, then return, to **Trust all and continue**; if the selection already starts there, return alone | Codex 0.157.1 |
|
|
199
|
+
| Codex's `/new`: `Where should the new conversation run?` | `\r` | return: its selection starts on **Current checkout** (bot home) | Codex 0.157.1 |
|
|
200
|
+
| Codex's update offer, `1. Update now` | `\r` | return: its selection starts on **Update now**, which accepts it | Codex 0.156.1; 0.157.1 has had no newer release to offer |
|
|
201
|
+
| `[oh-my-zsh] Would you like to update?` | `n` | they update their own shell | oh-my-zsh bf77e35 |
|
|
202
|
+
| Claude Code's `Teach auto mode about your environment?` | `\x1b` | Esc, which is **Not now**: it would write settings of theirs | Not yet proven: Esc is Not now in Claude Code 2.1.283's code; last seen live on 2.1.278, answered with 2. Not now |
|
|
203
|
+
|
|
204
|
+
Each key counts on where the selection starts, as it did on the version named.
|
|
205
|
+
Read the screen in front of you before you send: if the selection is somewhere
|
|
206
|
+
else, or the options have moved, go to the answer with the arrows and then
|
|
207
|
+
return. Never send a digit and then return: on Codex's update offer that took
|
|
208
|
+
the highlighted **Update now**, whatever the digit was.
|
|
207
209
|
|
|
208
210
|
Three things to know rather than guess at.
|
|
209
211
|
|
|
@@ -215,8 +217,7 @@ empty book and nothing says why.
|
|
|
215
217
|
Codex's trust question applies to the **repository root**, which for a bot means
|
|
216
218
|
the whole bots folder rather than the one bot. That is what they are agreeing to.
|
|
217
219
|
|
|
218
|
-
Codex's `/new` question (seen on 0.156.1
|
|
219
|
-
seen so far) is always answered with the current checkout, which is the bot
|
|
220
|
+
Codex's `/new` question (seen on 0.156.1 and 0.157.1) is always answered with the current checkout, which is the bot
|
|
220
221
|
home. The kit never makes a git worktree, so
|
|
221
222
|
`2. New worktree` is never the answer.
|
|
222
223
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assuredloop/orca-bot-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
26
|
"test": "node --test test/*.test.js",
|
|
27
|
+
"test:shard": "node --test --test-shard=$SHARD test/*.test.js",
|
|
27
28
|
"test:system": "node scripts/test-system.js",
|
|
28
29
|
"mutate": "node scripts/mutate.js"
|
|
29
30
|
},
|
package/rules/limits.md
CHANGED
|
@@ -17,6 +17,6 @@ publishing, releasing, spending money, anything another person receives.
|
|
|
17
17
|
When something you were told to use is missing (a model, a tool, a file), say
|
|
18
18
|
so and ask. Quietly using a different one hides the change.
|
|
19
19
|
|
|
20
|
-
Permission rules in a harness's settings
|
|
21
|
-
the user's yes to the exact rules: `obk bot change --allow <rule>`
|
|
22
|
-
|
|
20
|
+
Permission rules in a harness's settings or rules files are the kit's to write,
|
|
21
|
+
after the user's yes to the exact rules: `obk bot change --allow <rule>` or
|
|
22
|
+
`--disallow <rule>`. Never edit one in or out yourself, Bot Father included.
|
|
@@ -201,16 +201,41 @@ say that you edited it by hand.
|
|
|
201
201
|
Run `obk --help` for the flags of the version actually installed rather than
|
|
202
202
|
trusting a line you remember.
|
|
203
203
|
|
|
204
|
-
A bot
|
|
205
|
-
stop it for what the kit's own rules tell it to do: its
|
|
206
|
-
reading a long message kept beside the bots folder, and
|
|
207
|
-
create`, `obk up` and `obk rules build` list the rules
|
|
208
|
-
and give one command that allows them. Show the user
|
|
209
|
-
and say in a line what each lets the bot do. Run the
|
|
210
|
-
yes: it records the yes in the bot's `bot.yaml` and
|
|
211
|
-
|
|
212
|
-
rules
|
|
213
|
-
|
|
204
|
+
A bot needs a few permission rules so that auto mode, or Codex's sandbox and
|
|
205
|
+
reviewer, does not stop it for what the kit's own rules tell it to do: its
|
|
206
|
+
mail through the kit, reading a long message kept beside the bots folder, and
|
|
207
|
+
its commits. `obk bot create`, `obk up` and `obk rules build` list the rules
|
|
208
|
+
still waiting for a bot and give one command that allows them. Show the user
|
|
209
|
+
those rules word for word and say in a line what each lets the bot do. Run the
|
|
210
|
+
command only after their yes: it records the yes in the bot's `bot.yaml` and
|
|
211
|
+
writes the rules into its settings, or for Codex into its
|
|
212
|
+
`.codex/rules/obk.rules`. On a no, run nothing, and nothing is written. When
|
|
213
|
+
the kit lists new rules for a bot later, ask again. Never write a permission
|
|
214
|
+
rule into a settings or rules file by hand.
|
|
215
|
+
|
|
216
|
+
The charter decides what else the bot needs. What it lets the bot do without
|
|
217
|
+
asking, such as merging pull requests, auto mode or Codex's sandbox can still
|
|
218
|
+
stop, so each grant needs its own rule. When the charter is written, and again
|
|
219
|
+
whenever it changes, list the exact rules it grants: `Bash(gh pr merge:*)` for
|
|
220
|
+
a bot that merges pull requests, `Bash(gh issue close:*)` for one that closes
|
|
221
|
+
issues. Spell each command as the bot will run it, the kit by the path the
|
|
222
|
+
bot's rules give it and every folder absolute, or the rule will not match.
|
|
223
|
+
Spell them in Claude Code's form for a Codex bot too, `Bash(<command>:*)`: the
|
|
224
|
+
kit writes Codex's own form of each, and says which ones Codex has none for,
|
|
225
|
+
such as an `Edit` rule or one exact command.
|
|
226
|
+
Show the user the list word for word, with a line on what each lets the bot do,
|
|
227
|
+
and only after their yes run `obk bot change --allow`, once per rule they said
|
|
228
|
+
yes to. On a no, run nothing. `obk bot change --charter` names the rules the
|
|
229
|
+
bot is allowed now; say which of them the new charter no longer grants, show
|
|
230
|
+
each word for word, and ask whether to take it back. Only after their yes run
|
|
231
|
+
`obk bot change --disallow`, once per rule, which takes it out of `bot.yaml`
|
|
232
|
+
and the bot's settings or Codex rules; on a no, run nothing, and it stays
|
|
233
|
+
allowed. A rule the user added to a settings file by hand is theirs to take
|
|
234
|
+
out; the kit refuses it. Keep
|
|
235
|
+
each rule narrow: a program and what it does. The kit refuses a rule that lets
|
|
236
|
+
the bot run any command, a program with any arguments, or a shell or
|
|
237
|
+
interpreter with any arguments; a user who wants one for a bot adds it to that
|
|
238
|
+
bot's settings themselves. Propose no rule for what the charter does not grant.
|
|
214
239
|
|
|
215
240
|
A bot that should learn across its sessions can use its harness's own memory,
|
|
216
241
|
turned on for that bot alone, whatever the user's own setting is for everything
|
|
@@ -256,23 +281,31 @@ otherwise `/Applications/Orca.app/Contents/Resources/bin/orca`. A bare
|
|
|
256
281
|
`orca` can fail. Send each answer as one `<orca> terminal send --terminal
|
|
257
282
|
<handle> --text …`, with the return inside the text and no `--enter`:
|
|
258
283
|
|
|
259
|
-
| On screen | Send | Which is |
|
|
260
|
-
|
|
261
|
-
| Claude Code's
|
|
262
|
-
| Codex's `Trust this folder
|
|
263
|
-
| Codex's `Hooks need review` |
|
|
264
|
-
| Codex's
|
|
265
|
-
| Codex's update offer, `1. Update now` |
|
|
266
|
-
| `[oh-my-zsh] Would you like to update?` | `n` | no: the
|
|
267
|
-
| Claude Code's `Teach auto mode
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
284
|
+
| On screen | Send | Which is | Proven on |
|
|
285
|
+
|---|---|---|---|
|
|
286
|
+
| Claude Code's trust list | `\x1b[B\r` | down, return: off **No, exit** | Claude Code 2.1.283 |
|
|
287
|
+
| Codex's `Trust this folder?` | `\r` | return: it starts on yes | Codex 0.157.1 |
|
|
288
|
+
| Codex's `Hooks need review` | `\x1b[B\r` | **Trust all and continue** | Codex 0.157.1 |
|
|
289
|
+
| Codex's `Where should the new conversation run?` | `\r` | **Current checkout** | Codex 0.157.1 |
|
|
290
|
+
| Codex's update offer, `1. Update now` | `\r` | **Update now**, where it starts | Codex 0.156.1 |
|
|
291
|
+
| `[oh-my-zsh] Would you like to update?` | `n` | no: the user updates it | oh-my-zsh bf77e35 |
|
|
292
|
+
| Claude Code's `Teach auto mode…` | `\x1b` | Esc, **Not now** | Not yet proven, below |
|
|
293
|
+
|
|
294
|
+
Each key counts on where the selection starts on the version named. Read the
|
|
295
|
+
screen before you send: if the selection is elsewhere or the options have
|
|
296
|
+
moved, go to the answer with the arrows, then return. Never a digit and then
|
|
297
|
+
return: on Codex's update offer that took the highlighted **Update now**,
|
|
298
|
+
whatever the digit was. Codex's trust said `1. Yes, continue` on older
|
|
299
|
+
versions. Its hooks review sometimes starts on `2` already: then return alone.
|
|
300
|
+
0.157.1 has had no newer release to offer, so its update offer was proven on
|
|
301
|
+
0.156.1. Claude Code's `Teach auto mode` has not been seen live on 2.1.283:
|
|
302
|
+
Esc is Not now in its code, and 2.1.278 was answered with `2. Not now`.
|
|
303
|
+
Not now, because Yes writes the user's settings. Codex's hooks
|
|
271
304
|
question matters most. The kit's hook is how the book learns which
|
|
272
305
|
conversation the session is running, and until it is answered the
|
|
273
306
|
conversation has not started. Codex's trust applies to the repository root,
|
|
274
307
|
which is the whole bots folder. Codex's `/new` may ask where the new
|
|
275
|
-
conversation runs (0.156.1
|
|
308
|
+
conversation runs (0.156.1 and 0.157.1 both did); the answer is
|
|
276
309
|
always the current checkout, the bot home, and never `2. New worktree`:
|
|
277
310
|
the kit never makes a git worktree.
|
|
278
311
|
|
package/src/book.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// theirs. Beside each session's tab it holds the harness session id that
|
|
7
7
|
// session is running under, and every id it ran under before.
|
|
8
8
|
|
|
9
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { DatabaseSync } from 'node:sqlite';
|
|
12
12
|
import { isDeepStrictEqual } from 'node:util';
|
|
@@ -141,18 +141,26 @@ export async function updateBook(home, change) {
|
|
|
141
141
|
* empty and leaves no journal beside it.
|
|
142
142
|
*/
|
|
143
143
|
function takeLock(home) {
|
|
144
|
-
|
|
144
|
+
try {
|
|
145
|
+
return lockOn(lockFile(home), WAIT_MS);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
throw waitedTooLong(home, error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The lock on `file`, waited for `waitMs` at most: see `takeLock` for why it is this one. */
|
|
152
|
+
function lockOn(file, waitMs) {
|
|
145
153
|
mkdirSync(path.dirname(file), { recursive: true });
|
|
146
154
|
|
|
147
155
|
const db = new DatabaseSync(file);
|
|
148
156
|
try {
|
|
149
157
|
// A writer that arrives while another is working waits for it rather than
|
|
150
158
|
// failing at once, and gives up saying so rather than waiting for ever.
|
|
151
|
-
db.exec(`PRAGMA busy_timeout = ${
|
|
159
|
+
db.exec(`PRAGMA busy_timeout = ${waitMs}`);
|
|
152
160
|
db.exec('BEGIN IMMEDIATE');
|
|
153
161
|
} catch (error) {
|
|
154
162
|
db.close();
|
|
155
|
-
throw
|
|
163
|
+
throw error;
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
return {
|
|
@@ -183,12 +191,65 @@ function takeLock(home) {
|
|
|
183
191
|
* is nobody's to sweep, and the user can see it and delete it.
|
|
184
192
|
*
|
|
185
193
|
* One folder per bots folder, so two bots folders each holding an api-bot cannot
|
|
186
|
-
* take each other's turn.
|
|
194
|
+
* take each other's turn. Resolved first, as the mailbox's is: one writer reaches
|
|
195
|
+
* the bots folder through a link and another by its real path, and two paths
|
|
196
|
+
* would be two locks for one book (#375).
|
|
187
197
|
*/
|
|
188
|
-
const lockFile = (home) =>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
198
|
+
const lockFile = (home) => {
|
|
199
|
+
const real = realpathSync(home);
|
|
200
|
+
return path.join(
|
|
201
|
+
`${path.dirname(path.dirname(real))}.locks`,
|
|
202
|
+
`${encodeURIComponent(path.basename(real))}.lock`,
|
|
203
|
+
);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* One session's turn at its mailbox (#321): held by `session mailbox` for its
|
|
208
|
+
* whole step, by `message check` in the session's own tab for its bind, read and
|
|
209
|
+
* ack, and by `up` while it writes a new tab for the session into the book. So
|
|
210
|
+
* the book never moves to another tab while a step or a check is at work, and a
|
|
211
|
+
* step or check that waited reads the book again once it has its turn: two
|
|
212
|
+
* starts of one session at once leave its mailbox bound to the tab the book
|
|
213
|
+
* names, and nothing is read from the one it no longer does.
|
|
214
|
+
*
|
|
215
|
+
* A lock of its own, and not the book's: the book's is held for one read and one
|
|
216
|
+
* write, and a session's hook must never wait behind Orca for it. Taken before
|
|
217
|
+
* the book's and never while holding it, so neither waits on the other.
|
|
218
|
+
*
|
|
219
|
+
* Returns `{ release }`, or undefined when the turn did not come in
|
|
220
|
+
* `MAILBOX_WAIT_MS`: the caller says what it left, and a launch line goes on
|
|
221
|
+
* to its harness whatever happened here.
|
|
222
|
+
*/
|
|
223
|
+
export function takeMailboxTurn(home, session) {
|
|
224
|
+
try {
|
|
225
|
+
return lockOn(mailboxLockFile(home, session), MAILBOX_WAIT_MS);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (error.errcode === SQLITE_BUSY) return undefined;
|
|
228
|
+
throw error;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* How long a step, a check or an `up` waits for a session's turn. Longer than a
|
|
234
|
+
* step can hold it — three Orca calls of twenty seconds each at most — and short
|
|
235
|
+
* enough that a harness behind a stuck one still starts.
|
|
236
|
+
*/
|
|
237
|
+
export const MAILBOX_WAIT_MS = 60_000;
|
|
238
|
+
|
|
239
|
+
/** SQLite's own code for a lock another connection holds. */
|
|
240
|
+
const SQLITE_BUSY = 5;
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Beside the book's lock, one file per session. Names are lower-case letters,
|
|
244
|
+
* digits and hyphens, so a dot between the bot's and the session's cannot be
|
|
245
|
+
* read two ways. Resolved first, because the same bot is reached through a
|
|
246
|
+
* link by one command and by its real path by another, and two paths would be
|
|
247
|
+
* two turns.
|
|
248
|
+
*/
|
|
249
|
+
const mailboxLockFile = (home, session) => {
|
|
250
|
+
const real = realpathSync(home);
|
|
251
|
+
return path.join(`${path.dirname(path.dirname(real))}.locks`, `${path.basename(real)}.${session}.mailbox.lock`);
|
|
252
|
+
};
|
|
192
253
|
|
|
193
254
|
/**
|
|
194
255
|
* Something else has been writing the book for longer than this run is prepared
|
package/src/bot.js
CHANGED
|
@@ -254,14 +254,14 @@ export function changeBot(bots, bot, { charter }) {
|
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
/**
|
|
257
|
-
* The bot's `allow` list as it is, after refusing what `allowRules`
|
|
258
|
-
* an empty rule, or a list already there that is
|
|
259
|
-
* before anything is written, so a refused `bot
|
|
260
|
-
* charter included.
|
|
257
|
+
* The bot's `allow` list as it is, after refusing what `allowRules` or
|
|
258
|
+
* `disallowRules` would refuse: an empty rule, or a list already there that is
|
|
259
|
+
* not a list of rules. Asked before anything is written, so a refused `bot
|
|
260
|
+
* change` changes nothing, its charter included.
|
|
261
261
|
*/
|
|
262
|
-
export function allowedNow(bots, bot, rules) {
|
|
262
|
+
export function allowedNow(bots, bot, rules, flag = '--allow') {
|
|
263
263
|
if (rules.some((rule) => rule.trim() === '')) {
|
|
264
|
-
throw new Error(
|
|
264
|
+
throw new Error(`${flag} is empty. Give it the exact permission rule the user said yes to, such as Bash(git add:*).`);
|
|
265
265
|
}
|
|
266
266
|
const home = existingBot(bots, bot);
|
|
267
267
|
const was = readBot(home, bot).allow ?? [];
|
|
@@ -289,6 +289,21 @@ export function allowRules(bots, bot, rules) {
|
|
|
289
289
|
return { bot, home, allow, added };
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Take the permission rules the user said yes to taking back, `rules`, out of
|
|
294
|
+
* the bot's `allow` (#360). Returns { bot, home, allow, disallowed }: the whole
|
|
295
|
+
* list now, and what this call took out. The caller has refused a rule `allow`
|
|
296
|
+
* does not hold, and takes them out of the harness's files first. With `write`
|
|
297
|
+
* false it only refuses what the edit would refuse, and writes nothing.
|
|
298
|
+
*/
|
|
299
|
+
export function disallowRules(bots, bot, rules, { write = true } = {}) {
|
|
300
|
+
const { home, was } = allowedNow(bots, bot, rules, '--disallow');
|
|
301
|
+
const disallowed = [...new Set(rules)];
|
|
302
|
+
const allow = was.filter((rule) => !disallowed.includes(rule));
|
|
303
|
+
editBot(bots, bot, `take back ${disallowed.join(', ')} for ${bot}`, (doc) => doc.set('allow', allow), (before) => ({ ...before, allow }), write);
|
|
304
|
+
return { bot, home, allow, disallowed };
|
|
305
|
+
}
|
|
306
|
+
|
|
292
307
|
/**
|
|
293
308
|
* Change the settings of the session `name` of the bot `bot`. Returns
|
|
294
309
|
* { bot, home, session } — the session as it now reads.
|
|
@@ -387,9 +402,10 @@ function editSession(bots, bot, index, session, what) {
|
|
|
387
402
|
|
|
388
403
|
/**
|
|
389
404
|
* Make one edit to a bot's `bot.yaml` through the YAML library, and write it
|
|
390
|
-
* only if exactly the change `expected` describes is what came out
|
|
405
|
+
* only if exactly the change `expected` describes is what came out, and
|
|
406
|
+
* `write` is not false.
|
|
391
407
|
*/
|
|
392
|
-
function editBot(bots, bot, what, edit, expected) {
|
|
408
|
+
function editBot(bots, bot, what, edit, expected, write = true) {
|
|
393
409
|
const file = path.join(existingBot(bots, bot), BOT_YAML);
|
|
394
410
|
const source = readFileSync(file, 'utf8');
|
|
395
411
|
const doc = parseDocument(source);
|
|
@@ -398,7 +414,7 @@ function editBot(bots, bot, what, edit, expected) {
|
|
|
398
414
|
if (!changesExactly(source, text, expected)) {
|
|
399
415
|
throw new Error(`${file} cannot be changed to ${what} without changing something else in it, so nothing was written. Make the change by hand.`);
|
|
400
416
|
}
|
|
401
|
-
if (text !== source) writeFileSync(file, text);
|
|
417
|
+
if (write && text !== source) writeFileSync(file, text);
|
|
402
418
|
}
|
|
403
419
|
|
|
404
420
|
/** A bot that is there, by its home, or the refusal a caller who named another is owed. */
|