@bongos/core 1.21.79 → 1.21.80
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 +28 -23
- package/docs/adr/0155-adopt-private-repo-widen-oauth-scope.md +1 -0
- package/docs/adr/0370-a-repo-we-create-is-private.md +29 -0
- package/docs/adr/README.md +1 -0
- package/docs/copy-inventory.md +2 -2
- package/docs/copy-registry.json +11 -11
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-readings.json +3 -3
- package/modules/hall-ui/public/tweak-editor-lib.js +22 -3
- package/modules/hall-ui/records/tweak-editor.md +1 -1
- package/modules/public-landing/public/projects.html +5 -5
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +10 -0
- package/src/bongos/github-repos.js +7 -6
- package/src/module-api.js +1 -1
- package/tests/github_repos_helpers.mjs +5 -5
- package/tests/hall_tweak_editor.mjs +12 -0
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.21.
|
|
6
|
-
"core_contract": "1.21.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.21.80",
|
|
6
|
+
"core_contract": "1.21.80",
|
|
7
|
+
"source_commit": "b836a5577c82fe4d7594fc0a26a3e4f13076750a",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-10-
|
|
9
|
+
"built_at": "2026-10-09T00:19:50.271Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 590,
|
|
13
13
|
"agent_docs_stubbed": 28,
|
|
14
14
|
"functional_verbatim": 2992,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 3611,
|
|
20
|
+
"tree_sha256": "669bec1b067c38b5bf3b4bc4dd955774f73039820ab806892e1f5a05305813d8",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -1212,7 +1212,7 @@
|
|
|
1212
1212
|
{
|
|
1213
1213
|
"path": "docs/adr/0155-adopt-private-repo-widen-oauth-scope.md",
|
|
1214
1214
|
"mode": "0000644",
|
|
1215
|
-
"sha256": "
|
|
1215
|
+
"sha256": "81248d32dd28da671a5d006804359a43f5d373366a6704a14af906220d0ec5ec"
|
|
1216
1216
|
},
|
|
1217
1217
|
{
|
|
1218
1218
|
"path": "docs/adr/0156-user-set-display-name-wins.md",
|
|
@@ -2304,10 +2304,15 @@
|
|
|
2304
2304
|
"mode": "0000644",
|
|
2305
2305
|
"sha256": "10d2509453f6ab9c511adb9ca2e01927fdfbcb5d854830c66774cd07bc340a7c"
|
|
2306
2306
|
},
|
|
2307
|
+
{
|
|
2308
|
+
"path": "docs/adr/0370-a-repo-we-create-is-private.md",
|
|
2309
|
+
"mode": "0000644",
|
|
2310
|
+
"sha256": "d4963a203bce58f5d5f422bab582e022bf2d2b33eaa854c0ee80b6b76ccff1ee"
|
|
2311
|
+
},
|
|
2307
2312
|
{
|
|
2308
2313
|
"path": "docs/adr/README.md",
|
|
2309
2314
|
"mode": "0000644",
|
|
2310
|
-
"sha256": "
|
|
2315
|
+
"sha256": "3a58f45b80d4311b5ae5458bc7d564301978b0cf3ba5c72c6244bc1ef6356e89"
|
|
2311
2316
|
},
|
|
2312
2317
|
{
|
|
2313
2318
|
"path": "docs/api-reference.md",
|
|
@@ -2342,12 +2347,12 @@
|
|
|
2342
2347
|
{
|
|
2343
2348
|
"path": "docs/copy-inventory.md",
|
|
2344
2349
|
"mode": "0000644",
|
|
2345
|
-
"sha256": "
|
|
2350
|
+
"sha256": "28408a7426fa0bdb7b01b17b47ce5f96ba0d7c3362f2059273405357d674370c"
|
|
2346
2351
|
},
|
|
2347
2352
|
{
|
|
2348
2353
|
"path": "docs/copy-registry.json",
|
|
2349
2354
|
"mode": "0000644",
|
|
2350
|
-
"sha256": "
|
|
2355
|
+
"sha256": "857a6a515ddfe47e441636556714d8bc527ceab06ffefe06a368a8ead96d190d"
|
|
2351
2356
|
},
|
|
2352
2357
|
{
|
|
2353
2358
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -2857,7 +2862,7 @@
|
|
|
2857
2862
|
{
|
|
2858
2863
|
"path": "docs/module-api-changelog.md",
|
|
2859
2864
|
"mode": "0000644",
|
|
2860
|
-
"sha256": "
|
|
2865
|
+
"sha256": "32d825702934aa11e29c66c61e8cc5d3b7ba8133217227656308140c05315062"
|
|
2861
2866
|
},
|
|
2862
2867
|
{
|
|
2863
2868
|
"path": "docs/modules-contract.md",
|
|
@@ -2947,7 +2952,7 @@
|
|
|
2947
2952
|
{
|
|
2948
2953
|
"path": "docs/page-readings.json",
|
|
2949
2954
|
"mode": "0000644",
|
|
2950
|
-
"sha256": "
|
|
2955
|
+
"sha256": "1c91405fcccab79ce2b67c9c74ca7114d9c2c1f282e2547e2366f20a5acd417f"
|
|
2951
2956
|
},
|
|
2952
2957
|
{
|
|
2953
2958
|
"path": "docs/project-context.template.md",
|
|
@@ -6387,7 +6392,7 @@
|
|
|
6387
6392
|
{
|
|
6388
6393
|
"path": "modules/hall-ui/public/tweak-editor-lib.js",
|
|
6389
6394
|
"mode": "0000644",
|
|
6390
|
-
"sha256": "
|
|
6395
|
+
"sha256": "0cc42c06e988b9d54d4443023d92c56b2fef8779823560735265c1e0f1a45ab7"
|
|
6391
6396
|
},
|
|
6392
6397
|
{
|
|
6393
6398
|
"path": "modules/hall-ui/public/tweak-editor.css",
|
|
@@ -6582,7 +6587,7 @@
|
|
|
6582
6587
|
{
|
|
6583
6588
|
"path": "modules/hall-ui/records/tweak-editor.md",
|
|
6584
6589
|
"mode": "0000644",
|
|
6585
|
-
"sha256": "
|
|
6590
|
+
"sha256": "6667b86da5c9e9259c047254866ee69f4b281ba3a941117eab7106a9f4aec513"
|
|
6586
6591
|
},
|
|
6587
6592
|
{
|
|
6588
6593
|
"path": "modules/ideas/CLAUDE.md",
|
|
@@ -9302,7 +9307,7 @@
|
|
|
9302
9307
|
{
|
|
9303
9308
|
"path": "modules/public-landing/public/projects.html",
|
|
9304
9309
|
"mode": "0000644",
|
|
9305
|
-
"sha256": "
|
|
9310
|
+
"sha256": "a67a0fb1211176b2375e652ab570342063d9f8e5eafd715a0469a42184f98e4d"
|
|
9306
9311
|
},
|
|
9307
9312
|
{
|
|
9308
9313
|
"path": "modules/public-landing/public/projects.probes.json",
|
|
@@ -10082,12 +10087,12 @@
|
|
|
10082
10087
|
{
|
|
10083
10088
|
"path": "package-lock.json",
|
|
10084
10089
|
"mode": "0000644",
|
|
10085
|
-
"sha256": "
|
|
10090
|
+
"sha256": "12eba8a8f76736d82a5a1cf8f991f43415491163325465b2197cac227c6c3544"
|
|
10086
10091
|
},
|
|
10087
10092
|
{
|
|
10088
10093
|
"path": "package.json",
|
|
10089
10094
|
"mode": "0000644",
|
|
10090
|
-
"sha256": "
|
|
10095
|
+
"sha256": "186a539924b6c2d566c4c23a84d99b3574567b41babe3eddc4e8f3406480e96e"
|
|
10091
10096
|
},
|
|
10092
10097
|
{
|
|
10093
10098
|
"path": "public-docs/index.html",
|
|
@@ -10107,7 +10112,7 @@
|
|
|
10107
10112
|
{
|
|
10108
10113
|
"path": "release-notes.json",
|
|
10109
10114
|
"mode": "0000644",
|
|
10110
|
-
"sha256": "
|
|
10115
|
+
"sha256": "fced1b5988cd7b0496a0f28a532cec8d233d2889b4ca667f2dc0485bf62fb678"
|
|
10111
10116
|
},
|
|
10112
10117
|
{
|
|
10113
10118
|
"path": "scripts/cron/style_guide_audit.sh",
|
|
@@ -12037,7 +12042,7 @@
|
|
|
12037
12042
|
{
|
|
12038
12043
|
"path": "src/bongos/github-repos.js",
|
|
12039
12044
|
"mode": "0000644",
|
|
12040
|
-
"sha256": "
|
|
12045
|
+
"sha256": "af5600f22a3421c8197e91c77d422e9bca789eb458789924e8d75a00818f314a"
|
|
12041
12046
|
},
|
|
12042
12047
|
{
|
|
12043
12048
|
"path": "src/bongos/goal-scope-check.js",
|
|
@@ -12357,7 +12362,7 @@
|
|
|
12357
12362
|
{
|
|
12358
12363
|
"path": "src/module-api.js",
|
|
12359
12364
|
"mode": "0000644",
|
|
12360
|
-
"sha256": "
|
|
12365
|
+
"sha256": "5e3a905a711427b31f0965112e50a85872a8d45c3bcf8e2c7f4c6b436206d436"
|
|
12361
12366
|
},
|
|
12362
12367
|
{
|
|
12363
12368
|
"path": "src/module-loader/catalog.js",
|
|
@@ -13997,7 +14002,7 @@
|
|
|
13997
14002
|
{
|
|
13998
14003
|
"path": "tests/github_repos_helpers.mjs",
|
|
13999
14004
|
"mode": "0000644",
|
|
14000
|
-
"sha256": "
|
|
14005
|
+
"sha256": "d0c12aab13423b92d3b1d1199ac75950da769f56d361853f4ab2f3eda98e6620"
|
|
14001
14006
|
},
|
|
14002
14007
|
{
|
|
14003
14008
|
"path": "tests/github_token_session_life.mjs",
|
|
@@ -14852,7 +14857,7 @@
|
|
|
14852
14857
|
{
|
|
14853
14858
|
"path": "tests/hall_tweak_editor.mjs",
|
|
14854
14859
|
"mode": "0000644",
|
|
14855
|
-
"sha256": "
|
|
14860
|
+
"sha256": "32164a5039c5989e25ff1c6dac77a9cb2d55daa3879fb638aa9b991e9cce5a84"
|
|
14856
14861
|
},
|
|
14857
14862
|
{
|
|
14858
14863
|
"path": "tests/handle_machinery.mjs",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
- **Date:** 2026-08-04
|
|
5
5
|
- **Deciders:** the owner chose the credential approach after being shown the trade-off; Claude diagnosed + built.
|
|
6
6
|
- **Amends:** [ADR 0143](0143-onboarding-repo-picker-oauth-scope.md) §1 (its Decision 1 — scope choice). 0143 otherwise stands unchanged: opt-in per sign-in, lazy consent, encrypted short-TTL token store, personal repos only, 503-degrade. Per 0143's own append-only rule, this is a new ADR, not an edit.
|
|
7
|
+
- **Superseded in part by:** [ADR 0370](0370-a-repo-we-create-is-private.md) — Decision 3 (a repo we create stays public) is reversed: a repo we create is private.
|
|
7
8
|
- **Related:** [ADR 0121](0121-greenfield-vs-brownfield-onboarding-adopt-existing-repo.md) (greenfield vs brownfield onboarding).
|
|
8
9
|
|
|
9
10
|
## Context
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# ADR 0370 — A repo we create for a new project is private
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-10-08
|
|
5
|
+
- **Task:** [task 1004645](https://cloudbongos.com/builders#/task/1004645) (goal 1000106, BONGOS-V2)
|
|
6
|
+
- **Supersedes:** [ADR 0155](0155-adopt-private-repo-widen-oauth-scope.md) Decision 3 ("a repo *we* create stays public"). The rest of 0155 stands.
|
|
7
|
+
- **Builds on:** [ADR 0176](0176-private-repo-deploy-keys.md) (a private target repo gets its own read-only deploy key before the first pull)
|
|
8
|
+
- **Decided with:** the owner, 2026-10-08: the public default is a major security issue, and the default must be private.
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
When the create wizard makes a new GitHub repository for a project, `createUserRepo` (`src/bongos/github-repos.js`) sent `private: false`. ADR 0155 kept that on purpose: a Cloud Bongos instance was meant to be open source by design, and an owner who wanted private code had to make the repo on GitHub themselves and adopt it.
|
|
13
|
+
|
|
14
|
+
That put every new project's code on the public internet from its first push, without the owner ever choosing it. A first-time owner does not know to take the side door. Anything they commit by mistake, such as a key, a customer file or unreleased work, is world-readable and may already be copied before they notice.
|
|
15
|
+
|
|
16
|
+
The reason the public default was once forced has gone. ADR 0155 widened the token to `repo`, which can create a private repo. ADR 0176 taught provisioning to stand up a private repo: `ensurePrivateRepoAccess` registers the project's own read-only deploy key right after the scaffold push and before the first `git pull`. The greenfield push already uses the owner's token over HTTPS. The owner creates the repo in their own account, so they are its admin, which is what registering a deploy key needs.
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
1. **`createUserRepo` sends `private: true`.** It is still not a parameter, so nothing can flip it by accident. The unit test pins `true` exactly; a dropped field would fall back to GitHub's own public default and red the test.
|
|
21
|
+
2. **The wizard says so.** The "A new repo" choice reads "Private, set up for you", and the name field's hint says the new repo is private and can be made public later.
|
|
22
|
+
3. **Going public stays the owner's choice.** The manage page's repo-visibility control (`makeRepoPublic`) is unchanged.
|
|
23
|
+
|
|
24
|
+
## Consequences
|
|
25
|
+
|
|
26
|
+
- A new project's code is private unless its owner makes it public.
|
|
27
|
+
- Every new cloud-hosted project now takes the private standup path: one extra deploy key per project, registered on the owner's repo. That path has run on adopted private repos since ADR 0176.
|
|
28
|
+
- Projects created before this change keep their public repos. Nothing flips an existing repo.
|
|
29
|
+
- An instance that wants to be open source now makes that choice on the manage page instead of getting it by default.
|
package/docs/adr/README.md
CHANGED
|
@@ -488,3 +488,4 @@ These 20 numbers are each shared by exactly two files. They are **accepted histo
|
|
|
488
488
|
| 0367 | [**The Discord support bot answers from public docs only, under a monthly cap that fails closed** ([task 1002534](https://cloudbongos.com/builders#/task/1002534), goal 1000110). **D1:** one pinned channel, only when @-mentioned; the first gateway reply. **D2:** the corpus is an allowlist (`README.md`, `docs/onboarding/`, `docs/api-reference.md`) served by a new `memory.searchPublicDocs` port whose scope (doc, xenos, no owner, allowlisted ref) is fixed in SQL; the asker is never looked up. **D3:** no writes but the reply and one `audit_log` row per answer, which is also the spend ledger, so no migration. **D4:** a nonce-fenced prompt, no tools, mentions disabled. **D5:** `claude-haiku-4-5`, `DISCORD_SUPPORT_MONTHLY_CAP_USD` default $5, fail closed, with in-flight answers reserved at worst case. **D6:** off by default; its own switch and an env-only, namespaced key. **D7:** the channel's humans review wrong answers; the fix is the doc.](0367-the-support-bot-answers-from-public-docs-only.md) | discord / support / spend |
|
|
489
489
|
| 0368 | [**Red and blue teams practise on a range, never on live** (goal 1000094 — *Working area 10, Security*; proposed). **D2:** the range is its own small server, rebuilt from the candidate the owner marks "stage for attack". **D3:** it shares no identity hub, secret or data with live. **D5:** red is AI plus Metic+, with one side at a time and a 30-day switch. **D6:** attacks run on subscriptions, never the metered API. **D7:** 3 runs on the newest model with 0 high+ breaks, advisory until an outside tenant exists. **D8:** points only, no credits yet. **D9:** three live signals to `#security-alerts`.](0368-red-and-blue-teams-practice-on-a-range-never-on-live.md) | security / red-team / range |
|
|
490
490
|
| 0369 | [**Ship mode decides who starts a ship, not how it lands** ([task 1004628](https://cloudbongos.com/builders#/task/1004628), goal 1000106). **D1:** a `ship.mode` project setting, `auto` (the AI ships and lands a finished task with no commands) or `manual` (a person approves each ship), in `project_settings` with no migration. **D2:** Auto is the default, and the resolver reports whether anyone has chosen. **D3:** it is separate from deploy mode (`config/deploy.json`), which decides how a land happens. **D4:** any builder can read it (the AI must know), and `project.settings.manage` writes it, from the settings page or `bongos ship-mode`.](0369-ship-mode-decides-who-starts-a-ship-not-how-it-lands.md) | lifecycle / shipping / project settings |
|
|
491
|
+
| 0370 | [**A repo we create for a new project is private** ([task 1004645](https://cloudbongos.com/builders#/task/1004645), goal 1000106). Supersedes ADR 0155 Decision 3: `createUserRepo` sends `private: true`, still not a parameter, and the wizard says the new repo is private. Safe because ADR 0176 already gives a private repo its own read-only deploy key before the first pull; the owner can make it public from the manage page.](0370-a-repo-we-create-is-private.md) | onboarding / security / github |
|
package/docs/copy-inventory.md
CHANGED
|
@@ -2334,6 +2334,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
2334
2334
|
| `d4e9bf625ab3` | certain | Pick one of your repositories | `modules/public-landing/public/projects.html:1717` |
|
|
2335
2335
|
| `082156998085` | certain | Private hides the page from everyone — including you: it answers “not found” to every visitor, and you read yo | `modules/public-landing/public/account.html:212` |
|
|
2336
2336
|
| `d0c4d9ddcd71` | certain | Private individual | `modules/public-landing/public/projects.html:1647` |
|
|
2337
|
+
| `1a89b96a6a62` | certain | Private, set up for you | `modules/public-landing/public/projects.html:1699` |
|
|
2337
2338
|
| `19b960fc4777` | certain | Project address | `modules/public-landing/public/guild.html:235` |
|
|
2338
2339
|
| `92c7055aa799` | certain | Project handle | `modules/public-landing/public/projects.html:1754` |
|
|
2339
2340
|
| `edab0ed81a40` | certain | Project name | `modules/public-landing/public/projects.html:1560` |
|
|
@@ -2344,7 +2345,6 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
2344
2345
|
| `392b3876e595` | certain | Public marketed project | `modules/public-landing/public/projects.html:1945` |
|
|
2345
2346
|
| `c03d80ba0788` | certain | Public or private | `modules/public-landing/public/projects.html:1703` |
|
|
2346
2347
|
| `eaece4f1b37b` | certain | Public or private both work, and we never change it. | `modules/public-landing/public/projects.html:1739` |
|
|
2347
|
-
| `cdd827b4fba0` | certain | Public, set up for you | `modules/public-landing/public/projects.html:1699` |
|
|
2348
2348
|
| `fe9d120093e8` | certain | Put this guild forward for a project. It applies for nobody: each member sees it in their invitations and appl | `modules/public-landing/public/guild.html:233` |
|
|
2349
2349
|
| `351a61f190a6` | certain | Questions go through the | `modules/public-landing/public/terms.html:131` |
|
|
2350
2350
|
| `664f5731ddc3` | certain | Read the source. | `modules/public-landing/public/privacy.html:125` |
|
|
@@ -2631,7 +2631,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
2631
2631
|
| `7a053a51ee5f` | certain | privacy page | `modules/public-landing/public/terms.html:131` |
|
|
2632
2632
|
| `f8aa3ff34a10` | certain | projects you build on | `modules/public-landing/public/account.html:258` |
|
|
2633
2633
|
| `646b1e72622d` | certain | read by a panel | `modules/public-landing/public/index.html:795` |
|
|
2634
|
-
| `
|
|
2634
|
+
| `06a464e9968d` | certain | repo. You can make it public later. | `modules/public-landing/public/projects.html:1729` |
|
|
2635
2635
|
| `a4af8410855c` | certain | resume it | `modules/public-landing/public/projects.html:1494` |
|
|
2636
2636
|
| `56787ad7373b` | certain | save profile | `modules/public-landing/public/account.html:179` |
|
|
2637
2637
|
| `ab02a01db9e2` | certain | set up GitHub sign-in | `modules/public-landing/public/projects.html:2009` |
|
package/docs/copy-registry.json
CHANGED
|
@@ -21791,6 +21791,15 @@
|
|
|
21791
21791
|
"origin": "html-text",
|
|
21792
21792
|
"confidence": "certain"
|
|
21793
21793
|
},
|
|
21794
|
+
{
|
|
21795
|
+
"id": "1a89b96a6a62",
|
|
21796
|
+
"surface": "landing",
|
|
21797
|
+
"text": "Private, set up for you",
|
|
21798
|
+
"file": "modules/public-landing/public/projects.html",
|
|
21799
|
+
"line": 1699,
|
|
21800
|
+
"origin": "html-text",
|
|
21801
|
+
"confidence": "certain"
|
|
21802
|
+
},
|
|
21794
21803
|
{
|
|
21795
21804
|
"id": "19b960fc4777",
|
|
21796
21805
|
"surface": "landing",
|
|
@@ -21881,15 +21890,6 @@
|
|
|
21881
21890
|
"origin": "html-text",
|
|
21882
21891
|
"confidence": "certain"
|
|
21883
21892
|
},
|
|
21884
|
-
{
|
|
21885
|
-
"id": "cdd827b4fba0",
|
|
21886
|
-
"surface": "landing",
|
|
21887
|
-
"text": "Public, set up for you",
|
|
21888
|
-
"file": "modules/public-landing/public/projects.html",
|
|
21889
|
-
"line": 1699,
|
|
21890
|
-
"origin": "html-text",
|
|
21891
|
-
"confidence": "certain"
|
|
21892
|
-
},
|
|
21893
21893
|
{
|
|
21894
21894
|
"id": "fe9d120093e8",
|
|
21895
21895
|
"surface": "landing",
|
|
@@ -24465,9 +24465,9 @@
|
|
|
24465
24465
|
"confidence": "certain"
|
|
24466
24466
|
},
|
|
24467
24467
|
{
|
|
24468
|
-
"id": "
|
|
24468
|
+
"id": "06a464e9968d",
|
|
24469
24469
|
"surface": "landing",
|
|
24470
|
-
"text": "repo.
|
|
24470
|
+
"text": "repo. You can make it public later.",
|
|
24471
24471
|
"file": "modules/public-landing/public/projects.html",
|
|
24472
24472
|
"line": 1729,
|
|
24473
24473
|
"origin": "html-text",
|
|
@@ -2990,5 +2990,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2990
2990
|
landed since 1.21.77 with no explicit bump. run 37828775583. (task 1002620)
|
|
2991
2991
|
1.21.79 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2992
2992
|
landed since 1.21.78 with no explicit bump. run 37838546662. (task 1002620)
|
|
2993
|
+
1.21.80 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2994
|
+
landed since 1.21.79 with no explicit bump. run 37864152237. (task 1002620)
|
|
2993
2995
|
---------------------------------------------------------------------------
|
|
2994
2996
|
```
|
package/docs/page-readings.json
CHANGED
|
@@ -3554,7 +3554,7 @@
|
|
|
3554
3554
|
"surface": "builders",
|
|
3555
3555
|
"title": "Studio",
|
|
3556
3556
|
"states_read": ["home","out","flow","flow-pick","status","words-offer","words-edit","reviews","full","full-back","approvals","approvals-before","approvals-phone","approvals-note","approved","sent-back"],
|
|
3557
|
-
"files_hash": "
|
|
3557
|
+
"files_hash": "b3e0c68d7d013d8e",
|
|
3558
3558
|
"reading_hash": "7fc21db3071ddf23",
|
|
3559
3559
|
"counts": {"lines":33,"placed":16,"shared":7,"unplaced":10},
|
|
3560
3560
|
"omitted": {"data":2},
|
|
@@ -3809,7 +3809,7 @@
|
|
|
3809
3809
|
"surface": "builders",
|
|
3810
3810
|
"title": "Tweak a page",
|
|
3811
3811
|
"states_read": ["doc","typed","chrome","submitted","picking","out"],
|
|
3812
|
-
"files_hash": "
|
|
3812
|
+
"files_hash": "db91f438f529d626",
|
|
3813
3813
|
"reading_hash": "7d311833361923c2",
|
|
3814
3814
|
"counts": {"lines":24,"placed":15,"shared":1,"unplaced":8},
|
|
3815
3815
|
"omitted": {"data":34},
|
|
@@ -4579,7 +4579,7 @@
|
|
|
4579
4579
|
"surface": "landing",
|
|
4580
4580
|
"title": "{{productName}} — projects",
|
|
4581
4581
|
"states_read": ["out-map","in-map","in-map-projects","in-map-profile","in-map-kbd","in-map-search","in-map-mine-on","in-table","in-table-by-builders","in-mine","in-manage","in-manage-address","in-manage-planet","in-manage-card","in-manage-look","in-manage-kind","in-manage-invite","in-manage-invite-handoff","in-manage-invite-guild","in-manage-app-live","in-manage-app-building","in-manage-app-failed","in-manage-app-retry","in-manage-app-offer","in-manage-app-connect","in-manage-snag","in-manage-gone","in-manage-missing","out-manage","in-new","in-new-fork-ticked","in-new-terms-refused","in-new-fork-demo","in-new-demo-what","in-new-demo-review-address","in-new-what","in-new-name-prefilled","in-new-checks","in-new-checks-filled","in-new-checks-entity","in-new-review","in-new-review-code","in-new-review-address","in-new-review-app","in-new-review-extras","in-new-review-team","in-new-review-run","in-new-review-confirmed","in-new-look","in-new-done","in-new-resume-offer","in-new-look-custom","out-new","out-map-projects","card","card-noart","card-self","in-manage-demo","in-mine-dns-pending","in-manage-dns-pending","in-mine-empty","card-floor","card-backdrop","in-new-done-live"],
|
|
4582
|
-
"files_hash": "
|
|
4582
|
+
"files_hash": "e2a18fac786e762c",
|
|
4583
4583
|
"reading_hash": "333a01dfff5fa4e0",
|
|
4584
4584
|
"counts": {"lines":910,"placed":223,"shared":21,"unplaced":666},
|
|
4585
4585
|
"omitted": {"data":141},
|
|
@@ -189,8 +189,27 @@
|
|
|
189
189
|
// another host than the editor's cannot be framed: a hall page can, a landing
|
|
190
190
|
// or status page cannot. For those the split view is a second window docked to
|
|
191
191
|
// the bottom half of the screen.
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
//
|
|
193
|
+
// The SIBLINGS (task 1004393): once the proxy answers the apex and status hosts
|
|
194
|
+
// with `frame-ancestors 'self' https://builders.<apex>` instead of SAMEORIGIN,
|
|
195
|
+
// those two pages may split in place too. Until then framing them shows the
|
|
196
|
+
// browser's refusal page, so FRAME_SIBLINGS stays false and flipping it is the
|
|
197
|
+
// step after the proxy change (the owner's steps are on task 1004393). Only the
|
|
198
|
+
// apex and status.<apex> are siblings, never another subdomain: the proxy keeps
|
|
199
|
+
// every other host unframeable, sign-in and settings included.
|
|
200
|
+
const FRAME_SIBLINGS = false;
|
|
201
|
+
function siblingOrigins(loc) {
|
|
202
|
+
const here = new URL(loc.href || loc.origin);
|
|
203
|
+
if (!/^builders\./.test(here.hostname)) return [];
|
|
204
|
+
const apex = here.host.replace(/^builders\./, '');
|
|
205
|
+
return [`${here.protocol}//${apex}`, `${here.protocol}//status.${apex}`];
|
|
206
|
+
}
|
|
207
|
+
function canFrame(url, loc, siblings = FRAME_SIBLINGS) {
|
|
208
|
+
try {
|
|
209
|
+
const origin = new URL(url, loc.href || loc.origin).origin;
|
|
210
|
+
if (origin === (loc.origin || new URL(loc.href).origin)) return true;
|
|
211
|
+
return siblings === true && siblingOrigins(loc).includes(origin);
|
|
212
|
+
} catch (_) { return false; }
|
|
194
213
|
}
|
|
195
214
|
function splitWindowFeatures(scr) {
|
|
196
215
|
const s = scr || {};
|
|
@@ -229,7 +248,7 @@
|
|
|
229
248
|
return null;
|
|
230
249
|
}
|
|
231
250
|
|
|
232
|
-
const api = { OPEN_WORDS, openChoice, clampSplit, splitFromPointer, splitFromKey, canFrame, splitWindowFeatures, HINT, nextLineKey, confirmedCount, hintText, confirmStoreKey, parseConfirmed, NOTES, sectionsOf, isChanged, noteFor, draftBody, changedCount, counterText, initialValues, rowsFor, liveUrl, uploadedLabel, uploadRefusal, displayTitle, pageIdFrom };
|
|
251
|
+
const api = { OPEN_WORDS, openChoice, clampSplit, splitFromPointer, splitFromKey, canFrame, FRAME_SIBLINGS, splitWindowFeatures, HINT, nextLineKey, confirmedCount, hintText, confirmStoreKey, parseConfirmed, NOTES, sectionsOf, isChanged, noteFor, draftBody, changedCount, counterText, initialValues, rowsFor, liveUrl, uploadedLabel, uploadRefusal, displayTitle, pageIdFrom };
|
|
233
252
|
if (typeof window !== 'undefined') window.OTBTweakEditor = api;
|
|
234
253
|
if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
235
254
|
})();
|
|
@@ -27,7 +27,7 @@ The paper is paper and the room is the room whatever the hall's theme: the stage
|
|
|
27
27
|
- **Boxes grow to their words** rather than the artboard's rows 1 or 2.
|
|
28
28
|
- **A refused upload opens a panel** (task 1004354, `#tw-refused`): the "is in" panel's glass and scrim over the whole window, a headline per refusal (`lib.uploadRefusal`), the server's words, the lines it names, "what to do", and "download this page again" or close (Escape too). An artist uploads from the bar, often far down the page, where the paper's notice was off screen. The notice stays on the paper after the panel closes. The kit has no file-upload action, so the panel is proven in `tests/hall_tweak_editor.mjs` and by shots of a real browser at 1440 and 390, not by a states.json state.
|
|
29
29
|
- **The check, the restore and Shift + Enter (task 1004385, owner-asked).** Beside each line a round restore button (only on a changed line: it puts the page's words back and takes the check off) and a round green check. A checked line takes a light green wash; the owner picked the check over a tint alone. **Shift + Enter** in a line checks it and lands in the next, and a green hint above the words says so (with "N of M checked"), because it is the quickest way through a page. Enter alone is unchanged. The checks are the artist's place-keeper and stay in the browser (`localStorage`, keyed by page and reading hash, so a page that moved never ticks the wrong words); the server has no such field and `WRITES` is unchanged. A page the caller cannot write has none of it. Rules in `tweak-editor-lib.js` (`nextLineKey`, `confirmedCount`, `parseConfirmed`, `confirmStoreKey`, `hintText`).
|
|
30
|
-
- **No page title or sub text at the top of the paper, and "open the page" is a menu (task 1004386, owner-asked).** The bar already names the page, so the paper opens on its first section; the h1 stays, visually hidden, for the one-h1 floor. "open the page" is a **split button** (owner-amended mid-build): the main half repeats the artist's last choice ("open the page ↗" for a new tab, "open in split view"), and the ▾ beside it opens **open in new tab** and **open in split view**. Click, keyboard and touch; hover alone opens nothing, because a hover menu closes when the pointer wanders and does not exist on touch. Split view has a **draggable divider** (mouse, touch, or the arrow keys and Home/End on the focused divider; the live page takes 20% to 80% of the window, `lib.clampSplit`), and the frame stops taking the pointer while it is dragged. The choice, the size and whether split view was open are remembered (`hall-tweak-open-as`, `hall-tweak-split-size`, `hall-tweak-split`), so the next page opens the same way; a split that would be a second window never reopens on load, because a browser blocks a popup nobody clicked. At phone width the live page takes at most half the screen, whatever the saved size, because the two-row bar is tall there. Split view pins the live page under the words (words above, page below, both scroll; "close split view" unloads it; the frame loads only when opened). **The live site answers `X-Frame-Options: SAMEORIGIN`** (set at the proxy in front of the app, not in this repo), so only a page on the editor's own host can be framed. A landing or status page is on another host, so its split view opens the page in a second window docked to the bottom half of the screen (`lib.canFrame`, `lib.splitWindowFeatures`).
|
|
30
|
+
- **No page title or sub text at the top of the paper, and "open the page" is a menu (task 1004386, owner-asked).** The bar already names the page, so the paper opens on its first section; the h1 stays, visually hidden, for the one-h1 floor. "open the page" is a **split button** (owner-amended mid-build): the main half repeats the artist's last choice ("open the page ↗" for a new tab, "open in split view"), and the ▾ beside it opens **open in new tab** and **open in split view**. Click, keyboard and touch; hover alone opens nothing, because a hover menu closes when the pointer wanders and does not exist on touch. Split view has a **draggable divider** (mouse, touch, or the arrow keys and Home/End on the focused divider; the live page takes 20% to 80% of the window, `lib.clampSplit`), and the frame stops taking the pointer while it is dragged. The choice, the size and whether split view was open are remembered (`hall-tweak-open-as`, `hall-tweak-split-size`, `hall-tweak-split`), so the next page opens the same way; a split that would be a second window never reopens on load, because a browser blocks a popup nobody clicked. At phone width the live page takes at most half the screen, whatever the saved size, because the two-row bar is tall there. Split view pins the live page under the words (words above, page below, both scroll; "close split view" unloads it; the frame loads only when opened). **The live site answers `X-Frame-Options: SAMEORIGIN`** (set at the proxy in front of the app, not in this repo), so only a page on the editor's own host can be framed. A landing or status page is on another host, so its split view opens the page in a second window docked to the bottom half of the screen (`lib.canFrame`, `lib.splitWindowFeatures`). **Letting them split in place (task 1004393)** is two steps, in this order. First the proxy answers the apex and `status.<apex>` with `<redacted>: frame-ancestors 'self' https://builders.<apex>` in place of `X-Frame-Options: SAMEORIGIN`, and every other host keeps SAMEORIGIN (sign-in and settings stay unframeable). Then `FRAME_SIBLINGS` in `tweak-editor-lib.js` flips to `true`. `lib.canFrame` then also admits exactly those two origins, same scheme and port, and only from the builders host; never another subdomain or a lookalike. Flipping it before the proxy changes would put the browser's refusal page in the split, which is why it ships off.
|
|
31
31
|
- **Not on the artboard at all:** the phone layout (the bar becomes a two-row glass card, notes drop under their lines), the shell's full-screen button top right, the read-only and refusal notices on the paper, the chrome view (the switch off), and light mode.
|
|
32
32
|
|
|
33
33
|
## The proof
|
|
@@ -1696,7 +1696,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1696
1696
|
<div class="choices" role="group" aria-label="Starting point">
|
|
1697
1697
|
<button type="button" class="choice" id="c-green" aria-pressed="true">
|
|
1698
1698
|
<b>A new repo</b>
|
|
1699
|
-
<p>
|
|
1699
|
+
<p>Private, set up for you</p>
|
|
1700
1700
|
</button>
|
|
1701
1701
|
<button type="button" class="choice" id="c-adopt" aria-pressed="false">
|
|
1702
1702
|
<b>A repo I have</b>
|
|
@@ -1726,7 +1726,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1726
1726
|
<input type="text" id="ghNewName" placeholder="moonbase" autocomplete="off" maxlength="100" aria-label="New repository name">
|
|
1727
1727
|
<!-- visibility is stated on EVERY repo path (task 1002721); updateFreshPreview()
|
|
1728
1728
|
replaces this floor with the same claim plus the resolved repo URL -->
|
|
1729
|
-
<div class="hint" id="ghFreshHint">A new <strong>
|
|
1729
|
+
<div class="hint" id="ghFreshHint">A new <strong>private</strong> repo. You can make it public later.</div>
|
|
1730
1730
|
</div>
|
|
1731
1731
|
<div class="row2" id="manualHome">
|
|
1732
1732
|
<label class="wizField"><span>GitHub owner <small>(user or org)</small></span>
|
|
@@ -5291,9 +5291,9 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5291
5291
|
hint.className = 'hint err'; hint.innerHTML = 'You already have a repo named <strong>' + esc(state.repo) + '</strong> — pick another name, or adopt it instead.';
|
|
5292
5292
|
} else {
|
|
5293
5293
|
/* Visibility stated up front (task 1002694 / idea 1000684): repos WE create are
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
hint.className = 'hint'; hint.innerHTML = 'A new <strong>
|
|
5294
|
+
private by default (github-repos.js, ADR 0370); the owner can make one
|
|
5295
|
+
public later from the project's manage page. */
|
|
5296
|
+
hint.className = 'hint'; hint.innerHTML = 'A new <strong>private</strong> repo at <code style="font-family:var(--font-mono)">github.com/' + esc(myLogin()) + '/' + esc(state.repo || 'name') + '</code>. You can make it public later.';
|
|
5297
5297
|
}
|
|
5298
5298
|
}
|
|
5299
5299
|
$('ghNewName').addEventListener('input', function () { state.repo = this.value.trim(); updateFreshPreview(); saveDraft(); });
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.80",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.80",
|
|
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.21.
|
|
3
|
+
"version": "1.21.80",
|
|
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
|
@@ -9296,5 +9296,15 @@
|
|
|
9296
9296
|
"id": "1004626",
|
|
9297
9297
|
"text": "Every tab in the hall sidebar now has its own icon. Before, nineteen tabs shared just four pictures - all nine Government pages wore the same shield and every Docs page the same sheet of paper - so the collapsed sidebar was ha"
|
|
9298
9298
|
}
|
|
9299
|
+
],
|
|
9300
|
+
"1.21.80": [
|
|
9301
|
+
{
|
|
9302
|
+
"id": "1004645",
|
|
9303
|
+
"text": "New projects now get a private GitHub repository by default. Before, the code of every new project was public on the internet from the first save. Owners can still make their repo public later from the project page, and existi"
|
|
9304
|
+
},
|
|
9305
|
+
{
|
|
9306
|
+
"id": "1004393",
|
|
9307
|
+
"text": "The tweak editor is ready to show landing and status pages side by side with the words, instead of in a separate window. It stays off until a small web-server change is made on the host; those steps are waiting in the owner's"
|
|
9308
|
+
}
|
|
9299
9309
|
]
|
|
9300
9310
|
}
|
|
@@ -75,10 +75,11 @@ async function listUserRepos(token, { q = '', fetchImpl = fetch } = {}) {
|
|
|
75
75
|
// no README/first commit (exactly the "leave it completely empty" the manual step
|
|
76
76
|
// demanded).
|
|
77
77
|
//
|
|
78
|
-
// private:
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
78
|
+
// private:true — a repo WE create is PRIVATE (ADR 0370, superseding ADR 0155 §3's
|
|
79
|
+
// public default): a new owner's code must never be world-readable from the first
|
|
80
|
+
// push without them choosing that. The standup already handles a private target —
|
|
81
|
+
// ensurePrivateRepoAccess mints the project's own read-only deploy key before the
|
|
82
|
+
// first pull (ADR 0176) — and the owner can make it public later from the manage page.
|
|
82
83
|
// Deliberately not a parameter — nothing should be able to flip this implicitly.
|
|
83
84
|
//
|
|
84
85
|
// Returns { full_name, html_url }. A 422 (name already exists / invalid) becomes a
|
|
@@ -87,7 +88,7 @@ async function createUserRepo(token, { name, fetchImpl = fetch } = {}) {
|
|
|
87
88
|
const res = await fetchImpl(`${GH_API}/user/repos`, {
|
|
88
89
|
method: 'POST',
|
|
89
90
|
headers: ghHeaders(token, { 'Content-Type': 'application/json' }),
|
|
90
|
-
body: JSON.stringify({ name, private:
|
|
91
|
+
body: JSON.stringify({ name, private: true, auto_init: false }),
|
|
91
92
|
});
|
|
92
93
|
if (res.status === 201) {
|
|
93
94
|
const r = await res.json();
|
|
@@ -131,7 +132,7 @@ async function getUserRepo(token, { owner, name, fetchImpl = fetch } = {}) {
|
|
|
131
132
|
|
|
132
133
|
// The visibility PATCH both directions share. Deliberately NOT exported: callers get
|
|
133
134
|
// one named function per direction, so the name stays the contract and no caller can
|
|
134
|
-
// pass a direction it did not mean (the createUserRepo
|
|
135
|
+
// pass a direction it did not mean (the createUserRepo fixed-visibility precedent).
|
|
135
136
|
async function setRepoPrivate(token, { owner, name, fetchImpl = fetch }, isPrivate) {
|
|
136
137
|
const res = await fetchImpl(`${GH_API}/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}`, {
|
|
137
138
|
method: 'PATCH',
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.80'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -92,7 +92,7 @@ test('listUserRepos: 401 → typed github_unauthorized', async () => {
|
|
|
92
92
|
);
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
-
test('createUserRepo: sends auto_init:false + private:
|
|
95
|
+
test('createUserRepo: sends auto_init:false + private:true, returns full_name/html_url', async () => {
|
|
96
96
|
let sentBody = null;
|
|
97
97
|
const fetchImpl = async (url, init) => {
|
|
98
98
|
sentBody = JSON.parse(init.body);
|
|
@@ -100,10 +100,10 @@ test('createUserRepo: sends auto_init:false + private:false, returns full_name/h
|
|
|
100
100
|
};
|
|
101
101
|
const out = await gh.createUserRepo('tok', { name: 'mercury', fetchImpl });
|
|
102
102
|
assert.equal(sentBody.auto_init, false, 'empty repo — no README/first commit');
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
assert.equal(sentBody.private,
|
|
103
|
+
// PRIVATE by default (ADR 0370, superseding ADR 0155 §3): a new owner's code is never
|
|
104
|
+
// world-readable from the first push unless they choose that. strictEqual on `true`, so
|
|
105
|
+
// a dropped field (GitHub's own default is public) reds here too.
|
|
106
|
+
assert.equal(sentBody.private, true, 'a repo WE create is private by default');
|
|
107
107
|
assert.equal(sentBody.name, 'mercury');
|
|
108
108
|
assert.deepEqual(out, { full_name: 'me/mercury', html_url: 'https://github.com/me/mercury' });
|
|
109
109
|
});
|
|
@@ -657,6 +657,18 @@ try {
|
|
|
657
657
|
assert.equal(lib.canFrame('https://builders.cloudbongos.com/studio', here), true);
|
|
658
658
|
assert.equal(lib.canFrame('https://cloudbongos.com/', here), false, 'the apex is another origin');
|
|
659
659
|
assert.equal(lib.canFrame('not a url', { origin: '', href: '' }), false);
|
|
660
|
+
// task 1004393: the apex and status pages split in place only once the proxy allows it.
|
|
661
|
+
assert.equal(lib.FRAME_SIBLINGS, false, 'off until the proxy sends frame-ancestors for the builders host');
|
|
662
|
+
assert.equal(lib.canFrame('https://status.cloudbongos.com/', here), false, 'the default follows the switch');
|
|
663
|
+
assert.equal(lib.canFrame('https://cloudbongos.com/pricing', here, true), true, 'the apex, switched on');
|
|
664
|
+
assert.equal(lib.canFrame('https://status.cloudbongos.com/', here, true), true, 'the status host, switched on');
|
|
665
|
+
assert.equal(lib.canFrame('https://box.cloudbongos.com/', here, true), false, 'never another subdomain');
|
|
666
|
+
assert.equal(lib.canFrame('https://cloudbongos.com.evil.example/', here, true), false, 'never a lookalike host');
|
|
667
|
+
assert.equal(lib.canFrame('http://cloudbongos.com/', here, true), false, 'never the other scheme');
|
|
668
|
+
const local = { origin: 'http://builders.localhost:3000', href: 'http://builders.localhost:3000/tweak-editor' };
|
|
669
|
+
assert.equal(lib.canFrame('http://localhost:3000/', local, true), true, 'the port travels with the apex');
|
|
670
|
+
const bare = { origin: 'https://cloudbongos.com', href: 'https://cloudbongos.com/builders/tweak-editor' };
|
|
671
|
+
assert.equal(lib.canFrame('https://status.cloudbongos.com/', bare, true), false, 'only the builders host frames its siblings');
|
|
660
672
|
assert.equal(lib.splitWindowFeatures({ availWidth: 1600, availHeight: 1000, availLeft: 0, availTop: 0 }), 'popup=yes,width=1600,height=500,left=0,top=500', 'the bottom half of the screen');
|
|
661
673
|
assert.match(lib.splitWindowFeatures(undefined), /^popup=yes,width=\d+,height=\d+,left=0,top=\d+$/);
|
|
662
674
|
});
|