@bongos/core 1.24.78 → 1.24.79
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/.bongos-core.json +14 -14
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/bongos/autobongos-host.js +19 -4
- package/scripts/bongos/claude-materialize.js +20 -1
- package/src/module-api.js +1 -1
- package/tests/autobongos_host.mjs +19 -1
- package/tests/claude_materialize.mjs +74 -0
package/.bongos-core.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/bongos/package-core.js",
|
|
5
|
-
"core_version": "1.24.
|
|
6
|
-
"core_contract": "1.24.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.24.79",
|
|
6
|
+
"core_contract": "1.24.79",
|
|
7
|
+
"source_commit": "405acb3b1cc879b556f7ddb1e967e06b62d233b2",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-11-
|
|
9
|
+
"built_at": "2026-11-10T03:09:43.494Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 642,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 4608,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "d19920ebcb60573097ef6c5da63bba71f63d3de057ef78e0dc8e7a099b8cca7e",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -3007,7 +3007,7 @@
|
|
|
3007
3007
|
{
|
|
3008
3008
|
"path": "docs/module-api-changelog.md",
|
|
3009
3009
|
"mode": "0000644",
|
|
3010
|
-
"sha256": "
|
|
3010
|
+
"sha256": "2d49ce9988bbc0a073d7ad7fe21d2c58b5fc941a66a479effc4d2390985ddb46"
|
|
3011
3011
|
},
|
|
3012
3012
|
{
|
|
3013
3013
|
"path": "docs/modules-contract.md",
|
|
@@ -11977,12 +11977,12 @@
|
|
|
11977
11977
|
{
|
|
11978
11978
|
"path": "package-lock.json",
|
|
11979
11979
|
"mode": "0000644",
|
|
11980
|
-
"sha256": "
|
|
11980
|
+
"sha256": "a8db2c589cc13f77ad9776d4d3dc47f32a750429107b7cfb9dba7966e846ad0c"
|
|
11981
11981
|
},
|
|
11982
11982
|
{
|
|
11983
11983
|
"path": "package.json",
|
|
11984
11984
|
"mode": "0000644",
|
|
11985
|
-
"sha256": "
|
|
11985
|
+
"sha256": "790dafb2905894c20eaeab34117e51fd70e13a003ffa11041629bf74f6a1c6a1"
|
|
11986
11986
|
},
|
|
11987
11987
|
{
|
|
11988
11988
|
"path": "public-docs/index.html",
|
|
@@ -12002,7 +12002,7 @@
|
|
|
12002
12002
|
{
|
|
12003
12003
|
"path": "release-notes.json",
|
|
12004
12004
|
"mode": "0000644",
|
|
12005
|
-
"sha256": "
|
|
12005
|
+
"sha256": "a411e653e79bf2669e0b39e09df38d5add210e641ab2019fd2b1fc254ed80274"
|
|
12006
12006
|
},
|
|
12007
12007
|
{
|
|
12008
12008
|
"path": "scripts/bongos/CLAUDE.md",
|
|
@@ -12142,7 +12142,7 @@
|
|
|
12142
12142
|
{
|
|
12143
12143
|
"path": "scripts/bongos/autobongos-host.js",
|
|
12144
12144
|
"mode": "0000755",
|
|
12145
|
-
"sha256": "
|
|
12145
|
+
"sha256": "fa8cc6245fb0e6f5bd63e15314de796e396e5a0a8b7ab7817aefac2d69b26d95"
|
|
12146
12146
|
},
|
|
12147
12147
|
{
|
|
12148
12148
|
"path": "scripts/bongos/autobongos-loop.js",
|
|
@@ -12282,7 +12282,7 @@
|
|
|
12282
12282
|
{
|
|
12283
12283
|
"path": "scripts/bongos/claude-materialize.js",
|
|
12284
12284
|
"mode": "0000644",
|
|
12285
|
-
"sha256": "
|
|
12285
|
+
"sha256": "a57c52758fb1e135bf907ac5e8eadf8c5cb0bd24d0da2fecc4aac07d6d567fab"
|
|
12286
12286
|
},
|
|
12287
12287
|
{
|
|
12288
12288
|
"path": "scripts/bongos/claude-md-template.js",
|
|
@@ -15997,7 +15997,7 @@
|
|
|
15997
15997
|
{
|
|
15998
15998
|
"path": "src/module-api.js",
|
|
15999
15999
|
"mode": "0000644",
|
|
16000
|
-
"sha256": "
|
|
16000
|
+
"sha256": "5aac32d3d3b7dceb4c3d8d83409290bbe01dacfa08029fc33b53db5df0c1b444"
|
|
16001
16001
|
},
|
|
16002
16002
|
{
|
|
16003
16003
|
"path": "src/module-loader/catalog.js",
|
|
@@ -16557,7 +16557,7 @@
|
|
|
16557
16557
|
{
|
|
16558
16558
|
"path": "tests/autobongos_host.mjs",
|
|
16559
16559
|
"mode": "0000644",
|
|
16560
|
-
"sha256": "
|
|
16560
|
+
"sha256": "76bef425e2b3adcc4317b383636bdb3e81de64ae3e9fd2ae3ab09c5483bd8171"
|
|
16561
16561
|
},
|
|
16562
16562
|
{
|
|
16563
16563
|
"path": "tests/autobongos_host_access.mjs",
|
|
@@ -17002,7 +17002,7 @@
|
|
|
17002
17002
|
{
|
|
17003
17003
|
"path": "tests/claude_materialize.mjs",
|
|
17004
17004
|
"mode": "0000644",
|
|
17005
|
-
"sha256": "
|
|
17005
|
+
"sha256": "f37bebbff686710507474f4de7d74856f56f9698ae82b45319a2005cc29979b1"
|
|
17006
17006
|
},
|
|
17007
17007
|
{
|
|
17008
17008
|
"path": "tests/clawback_confirmed_backlog.mjs",
|
|
@@ -3406,5 +3406,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3406
3406
|
landed since 1.24.76 with no explicit bump. run 42735935883. (task 1002620)
|
|
3407
3407
|
1.24.78 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3408
3408
|
landed since 1.24.77 with no explicit bump. run 42746012589. (task 1002620)
|
|
3409
|
+
1.24.79 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3410
|
+
landed since 1.24.78 with no explicit bump. run 42747422335. (task 1002620)
|
|
3409
3411
|
---------------------------------------------------------------------------
|
|
3410
3412
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.79",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.24.
|
|
9
|
+
"version": "1.24.79",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.79",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -11507,5 +11507,11 @@
|
|
|
11507
11507
|
"id": "1005094",
|
|
11508
11508
|
"text": "Running Autobongos on a device is now one button. In Settings, Add a device gives you one line to paste on any Windows PC or Mac; its card then appears by itself with a Run button. The button's face tells you what the device i"
|
|
11509
11509
|
}
|
|
11510
|
+
],
|
|
11511
|
+
"1.24.79": [
|
|
11512
|
+
{
|
|
11513
|
+
"id": "1005186",
|
|
11514
|
+
"text": "Closed a gap opened by letting Autobongos run on any device. The runner now keeps its own sign-in in its own folder, and the rule that stops an AI session from opening sign-in files did not yet cover that folder. It does now,"
|
|
11515
|
+
}
|
|
11510
11516
|
]
|
|
11511
11517
|
}
|
|
@@ -74,16 +74,31 @@ const LOG_ROTATE_BYTES = 20 * 1024 * 1024;
|
|
|
74
74
|
function runnerHome(env = process.env, home = os.homedir(), repoRoot = REPO_ROOT) {
|
|
75
75
|
if (env.AUTOBONGOS_HOME) return path.resolve(env.AUTOBONGOS_HOME);
|
|
76
76
|
if (path.basename(repoRoot) === 'repo' && isRunnerCopy(repoRoot)) return path.dirname(repoRoot);
|
|
77
|
-
return path.join(home,
|
|
77
|
+
return path.join(home, RUNNER_DIR);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
//
|
|
81
|
-
//
|
|
80
|
+
// The runner's folder in the person's home. Named once: the read-deny rule that
|
|
81
|
+
// keeps agents out of the runner's login names this same folder
|
|
82
|
+
// (scripts/bongos/claude-materialize.js RUNNER_CONFIG_DENY), and a test holds the
|
|
83
|
+
// two together.
|
|
84
|
+
const RUNNER_DIR = '.cloudbongos-runner';
|
|
85
|
+
|
|
86
|
+
// hostPaths — every file this program owns. `configRoot` is handed to the
|
|
87
|
+
// supervisor as BONGOS_CONFIG_ROOT, which is what keeps its login apart.
|
|
88
|
+
//
|
|
89
|
+
// THE LOGIN LIVES IN ONE PLACE ON EVERY DEVICE (task 1005186): configRoot is always
|
|
90
|
+
// <home>/.cloudbongos-runner/config, WHATEVER AUTOBONGOS_HOME says. That variable
|
|
91
|
+
// moves the runner's copy of the project, its logs and its lock; it does not move
|
|
92
|
+
// the login. The reason is the deny rule: agents are refused reads under exactly
|
|
93
|
+
// that folder, and a login that could be put somewhere else by an environment
|
|
94
|
+
// variable would be a login the rule does not cover — on precisely the device
|
|
95
|
+
// someone had customised. One runner per device already, so there is nothing a
|
|
96
|
+
// second config folder would be for.
|
|
82
97
|
function hostPaths(env = process.env, home = os.homedir()) {
|
|
83
98
|
const root = runnerHome(env, home);
|
|
84
99
|
return {
|
|
85
100
|
root,
|
|
86
|
-
configRoot: path.join(
|
|
101
|
+
configRoot: path.join(home, RUNNER_DIR, 'config'),
|
|
87
102
|
logDir: path.join(root, 'logs'),
|
|
88
103
|
log: path.join(root, 'logs', 'autobongos.log'),
|
|
89
104
|
lock: path.join(root, 'host.json'),
|
|
@@ -481,10 +481,29 @@ const STARTER_DENY_SECRETS = [
|
|
|
481
481
|
'Read(**/*.pem)', 'Read(**/id_rsa*)', 'Read(**/id_ed25519*)',
|
|
482
482
|
];
|
|
483
483
|
const SESSION_STORE_DENY = `Read(~/.config/${ic.FALLBACK_DIR}/instances/**)`;
|
|
484
|
+
// An Autobongos runner keeps ITS OWN login and files in a folder of its own (task 1005095:
|
|
485
|
+
// BONGOS_CONFIG_ROOT, so adding a laptop as a runner never touches the builder's CLI session).
|
|
486
|
+
// That put the runner's session file, and the session store beside it, outside every rule above,
|
|
487
|
+
// which all stop at ~/.config — so on a runner device a worker could open the file and read the
|
|
488
|
+
// runner's login (task 1005186). The whole folder is denied rather than one file name: the
|
|
489
|
+
// runner's heartbeat and run log sit there too and no agent has a reason to read any of it, and
|
|
490
|
+
// a rule on the folder cannot be stepped around by a session file under a new name.
|
|
491
|
+
// The folder name is the device program's (scripts/bongos/autobongos-host.js hostPaths); it is
|
|
492
|
+
// spelled here rather than required from there because this file is the installer and must not
|
|
493
|
+
// load the runner, and tests/claude_materialize.mjs fails if the two ever name different places.
|
|
494
|
+
const RUNNER_CONFIG_DENY = 'Read(~/.cloudbongos-runner/config/**)';
|
|
495
|
+
|
|
496
|
+
// The rules that name a FIXED place, whatever the instance is called.
|
|
497
|
+
const FIXED_PLACE_DENY = [SESSION_STORE_DENY, RUNNER_CONFIG_DENY];
|
|
484
498
|
|
|
485
499
|
// A checkout's settings.json takes the identity rebrand like every copied file, and the store's
|
|
486
500
|
// dir is one of the core's `.config/<dir>` literals it rewrites. The store does not move with the
|
|
487
501
|
// brand (task 1003741), so its rule is put back where the CLI reads it (task 1004155).
|
|
502
|
+
//
|
|
503
|
+
// The runner rule needs no putting back: the rebrand rewrites `.config/<core dir>` literals, and
|
|
504
|
+
// the runner's folder is not one (measured: with the put-back removed, the three-brand case in
|
|
505
|
+
// tests/claude_materialize.mjs still holds). That case is what would notice a future rebrand
|
|
506
|
+
// rule broad enough to carry it off.
|
|
488
507
|
function rebrandSettings(text, rebrand) {
|
|
489
508
|
const moved = rebrand(SESSION_STORE_DENY);
|
|
490
509
|
return rebrand(text).split(JSON.stringify(moved)).join(JSON.stringify(SESSION_STORE_DENY));
|
|
@@ -504,7 +523,7 @@ function credentialDeny(branding = {}) {
|
|
|
504
523
|
return [
|
|
505
524
|
...STARTER_DENY_SECRETS,
|
|
506
525
|
...dirs.flatMap((dir) => credentialFiles.map((file) => `Read(~/.config/${dir}/${file})`)),
|
|
507
|
-
|
|
526
|
+
...FIXED_PLACE_DENY,
|
|
508
527
|
];
|
|
509
528
|
}
|
|
510
529
|
|
package/src/module-api.js
CHANGED
|
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
|
|
|
81
81
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
82
82
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
83
83
|
|
|
84
|
-
const CORE_VERSION = '1.24.
|
|
84
|
+
const CORE_VERSION = '1.24.79'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
85
85
|
|
|
86
86
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
87
87
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -221,6 +221,21 @@ test('the supervisor runs under the runner\'s own config root, and keeps a gauge
|
|
|
221
221
|
assert.equal(host.supervisorEnv({ AUTOBONGOS_GAUGE_FILE: '/set/by/hand' }, paths).AUTOBONGOS_GAUGE_FILE, '/set/by/hand');
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
+
test('AUTOBONGOS_HOME moves the runner\'s copy, logs and lock, and never the login', () => {
|
|
225
|
+
// The login must stay where the read-deny rule looks for it (task 1005186): a
|
|
226
|
+
// device someone customised is the last one that should fall outside the rule.
|
|
227
|
+
const home = path.join(tmp, 'fixed-login-home');
|
|
228
|
+
const standard = host.hostPaths({}, home);
|
|
229
|
+
const custom = host.hostPaths({ AUTOBONGOS_HOME: path.join(tmp, 'somewhere-else') }, home);
|
|
230
|
+
assert.equal(standard.configRoot, path.join(home, '.cloudbongos-runner', 'config'));
|
|
231
|
+
assert.equal(custom.configRoot, standard.configRoot, 'the same folder under the person\'s home, whatever the variable says');
|
|
232
|
+
assert.equal(custom.root, path.join(tmp, 'somewhere-else'));
|
|
233
|
+
assert.equal(custom.log, path.join(tmp, 'somewhere-else', 'logs', 'autobongos.log'));
|
|
234
|
+
assert.equal(custom.lock, path.join(tmp, 'somewhere-else', 'host.json'));
|
|
235
|
+
// And that is the folder the supervisor and its workers are pointed at.
|
|
236
|
+
assert.equal(host.supervisorEnv({ AUTOBONGOS_HOME: path.join(tmp, 'somewhere-else') }, custom).BONGOS_CONFIG_ROOT, standard.configRoot);
|
|
237
|
+
});
|
|
238
|
+
|
|
224
239
|
test('under BONGOS_CONFIG_ROOT every session path moves, and the builder\'s own session is not a fallback', () => {
|
|
225
240
|
const root = fresh('cfgroot');
|
|
226
241
|
const probe = `
|
|
@@ -636,7 +651,10 @@ test('the digest is read from the RUNNER’s run log: bare, the digest CLI looks
|
|
|
636
651
|
assert.deepEqual(argv.slice(1), ['--all', '--json'], 'whatever the person passed is handed through');
|
|
637
652
|
|
|
638
653
|
// Through the device program: the runner's night.
|
|
639
|
-
|
|
654
|
+
// The login folder is always under the person's home (task 1005186), so the
|
|
655
|
+
// child is given this test's stand-in home: HOME on a Mac or Linux, USERPROFILE
|
|
656
|
+
// on Windows. Otherwise it would read the real one.
|
|
657
|
+
const viaHost = spawnSync(process.execPath, [HOST_JS, 'digest', '--all', '--json'], { encoding: 'utf8', env: { ...process.env, HOME: home, USERPROFILE: home, AUTOBONGOS_HOME: home, AUTOBONGOS_LOG_FILE: '' } });
|
|
640
658
|
assert.equal(viaHost.status, 0, viaHost.stderr);
|
|
641
659
|
assert.ok(viaHost.stdout.includes('424242'), 'the task the runner built is in it');
|
|
642
660
|
|
|
@@ -951,6 +951,69 @@ t("the core's OWN deny list covers every file the CLI reads a session from, and
|
|
|
951
951
|
}
|
|
952
952
|
});
|
|
953
953
|
|
|
954
|
+
// ---- task 1005186: the runner's own login file is denied like the builder's ----
|
|
955
|
+
// Task 1005095 moved an Autobongos runner's session, heartbeat and run log under a folder of its
|
|
956
|
+
// own, so every deny rule above (all under ~/.config) stopped short of it and a worker on a
|
|
957
|
+
// runner device could read the runner's login.
|
|
958
|
+
const hostProgram = require('../scripts/bongos/autobongos-host.js');
|
|
959
|
+
const RUNNER_RULE = 'Read(~/.cloudbongos-runner/config/**)';
|
|
960
|
+
|
|
961
|
+
t('the runner deny rule names the folder the device program actually uses, however the runner was installed', () => {
|
|
962
|
+
// Asked of the device program with a stand-in home, so a renamed folder fails HERE rather
|
|
963
|
+
// than leaving a rule that guards a place nothing writes to.
|
|
964
|
+
const home = join(tmpdir(), 'runner-deny-home');
|
|
965
|
+
const ruleFor = (env) => `Read(~/${relative(home, hostProgram.hostPaths(env, home).configRoot).split(sep).join('/')}/**)`;
|
|
966
|
+
assert.equal(ruleFor({}), RUNNER_RULE, 'the default install');
|
|
967
|
+
// AUTOBONGOS_HOME moves the runner's copy, logs and lock. It must NOT move the login:
|
|
968
|
+
// a login an environment variable can relocate is one the rule does not cover.
|
|
969
|
+
for (const custom of [join(home, 'elsewhere'), join(tmpdir(), 'not-even-under-home'), join(home, '.cloudbongos-runner')]) {
|
|
970
|
+
const env = { AUTOBONGOS_HOME: custom };
|
|
971
|
+
assert.equal(ruleFor(env), RUNNER_RULE, `AUTOBONGOS_HOME=${custom}`);
|
|
972
|
+
assert.equal(hostProgram.hostPaths(env, home).root, custom, 'while the rest of the runner does move');
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
t('the runner deny rule covers the files a runner keeps there: its session, the store, its heartbeat', () => {
|
|
977
|
+
// The same cli-lib and supervisor a runner runs, under the runner's config root.
|
|
978
|
+
const home = join(tmpdir(), 'runner-deny-home');
|
|
979
|
+
const root = hostProgram.hostPaths({}, home).configRoot;
|
|
980
|
+
const probe = `
|
|
981
|
+
const cli = require(${JSON.stringify(join(ROOT, 'scripts', 'bongos', 'cli-lib.js'))});
|
|
982
|
+
const run = require(${JSON.stringify(join(ROOT, 'scripts', 'bongos', 'autobongos-run.js'))});
|
|
983
|
+
process.stdout.write(JSON.stringify([...cli.SESSION_READ_PATHS, cli.sessionStoreDir(), run.heartbeatPath(), run.logPath()]));`;
|
|
984
|
+
const r = require('node:child_process').spawnSync(process.execPath, ['-e', probe], { encoding: 'utf8', env: { ...process.env, BONGOS_CONFIG_ROOT: root, AUTOBONGOS_HEARTBEAT_FILE: '', AUTOBONGOS_LOG_FILE: '' } });
|
|
985
|
+
assert.equal(r.status, 0, r.stderr);
|
|
986
|
+
const files = JSON.parse(r.stdout);
|
|
987
|
+
assert.ok(files.length >= 5);
|
|
988
|
+
const guarded = `${root}${sep}`; // what Read(<root>/**) matches
|
|
989
|
+
for (const f of files) assert.ok(f.startsWith(guarded), `${f} sits outside the denied folder`);
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
t("the core's own deny list and the starter both carry the runner rule", () => {
|
|
993
|
+
const core = JSON.parse(readFileSync(join(ROOT, '.claude', 'settings.json'), 'utf8')).permissions.deny;
|
|
994
|
+
assert.ok(core.includes(RUNNER_RULE), 'the core checkout is itself where a runner works');
|
|
995
|
+
assert.ok(starterDenyFor({}).includes(RUNNER_RULE));
|
|
996
|
+
assert.ok(starterDenyFor({ configDir: 'acme', envPrefix: 'ACME' }).includes(RUNNER_RULE), 'at the same fixed place whatever the instance is called');
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
t('a checkout-materialized instance keeps the runner rule at its fixed place through the rebrand', () => {
|
|
1000
|
+
// An instance whose own name is the word in the folder's name: the rebrand must not
|
|
1001
|
+
// carry the rule off to a folder no runner uses.
|
|
1002
|
+
for (const branding of [{ configDir: 'acme', envPrefix: 'ACME' }, { configDir: 'cloudbongos' }, { configDir: 'otb', envPrefix: 'OTB' }]) {
|
|
1003
|
+
const core = mkdtempSync(join(tmpdir(), 'w3-runner-core-'));
|
|
1004
|
+
const inst = mkdtempSync(join(tmpdir(), 'w3-runner-inst-'));
|
|
1005
|
+
mkdirSync(join(core, '.claude', 'skills'), { recursive: true });
|
|
1006
|
+
writeFileSync(join(core, '.claude', 'settings.json'), JSON.stringify({ permissions: { deny: [RUNNER_RULE] } }));
|
|
1007
|
+
mkdirSync(join(inst, 'config'), { recursive: true });
|
|
1008
|
+
writeFileSync(join(inst, 'config', 'branding.json'), JSON.stringify(branding));
|
|
1009
|
+
m.materializeClaude({ coreRoot: core, instanceDir: inst, dryRun: false });
|
|
1010
|
+
const deny = JSON.parse(readFileSync(join(inst, '.claude', 'settings.json'), 'utf8')).permissions.deny;
|
|
1011
|
+
assert.deepEqual(deny, [RUNNER_RULE], JSON.stringify(branding));
|
|
1012
|
+
rmSync(core, { recursive: true, force: true });
|
|
1013
|
+
rmSync(inst, { recursive: true, force: true });
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
|
|
954
1017
|
t('a checkout-materialized instance keeps the session-store rule at its fixed anchor', () => {
|
|
955
1018
|
// The rebrand rewrites every `.config/<core dir>` literal, and the store's dir is one of them —
|
|
956
1019
|
// but the store does not move with the brand, so a rebranded rule guards a dir nothing reads.
|
|
@@ -1011,6 +1074,17 @@ t('an instance still on the allow-only starter GAINS the deny rules on its next
|
|
|
1011
1074
|
f.cleanup();
|
|
1012
1075
|
});
|
|
1013
1076
|
|
|
1077
|
+
t('an instance that already has a deny list gains the runner rule at its next upgrade (task 1005186)', () => {
|
|
1078
|
+
// Every rule it had before this task, and not the new one.
|
|
1079
|
+
const had = topUpDenyFor({ configDir: 'acme' }).filter((r) => r !== RUNNER_RULE);
|
|
1080
|
+
const f = topUpFixture({ permissions: { allow: m.STARTER_ALLOW, deny: had } });
|
|
1081
|
+
const res = m.materializeClaude({ coreRoot: f.core, instanceDir: f.inst, dryRun: false });
|
|
1082
|
+
const after = JSON.parse(f.read());
|
|
1083
|
+
assert.deepEqual(res.settingsDenyAdded, [RUNNER_RULE], 'the one rule it was missing, and it is named');
|
|
1084
|
+
assert.deepEqual(after.permissions.deny, [...had, RUNNER_RULE], 'appended; nothing it had is moved or dropped');
|
|
1085
|
+
f.cleanup();
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1014
1088
|
t("an owner-edited settings.json keeps every rule of its own and only gains what's missing", () => {
|
|
1015
1089
|
const f = topUpFixture({
|
|
1016
1090
|
permissions: { allow: ['Bash(npm test:*)'], deny: ['Read(secrets/**)', 'Read(**/*.pem)'], ask: ['Bash(rm:*)'] },
|