@orkestrel/scaffold 0.0.87 → 0.0.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/orkestrel-harden/scripts/discovery.js +483 -77
- package/dist/agents/skills/orkestrel-publish/scripts/window.js +32 -5
- package/dist/bin/main.js +4 -3
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/skills/orkestrel-harden/SKILL.md +3 -3
- package/dist/host/agents/skills/orkestrel-harden/scripts/discovery.ts +596 -76
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +1 -1
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +2 -0
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +2 -0
- package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +34 -5
- package/dist/host/claude/agents/orkestrel.md +2 -2
- package/dist/host/claude/rules/architecture.md +4 -0
- package/dist/host/claude/rules/tests.md +2 -0
- package/dist/host/claude/rules/workspace.md +1 -1
- package/dist/host/configs/policy.ts +13 -3
- package/dist/host/guides/browser.md +2626 -1195
- package/dist/host/guides/contract.md +1 -1
- package/dist/host/guides/scaffold.md +13 -3
- package/dist/host/manifest.json +16 -16
- package/dist/host/tests/config.test.ts +51 -1
- package/dist/src/core/index.cjs +138 -105
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +14 -14
- package/dist/src/core/index.d.ts +14 -14
- package/dist/src/core/index.js +138 -105
- package/dist/src/core/index.js.map +1 -1
- package/package.json +4 -3
|
@@ -25,7 +25,7 @@ description: Run an Orkestrel release from layer order to registry confirmation.
|
|
|
25
25
|
| `scripts/compare.ts` | `[--package NAME] [--version V] [--tarball PATH] [--dist dist] [--json]`, run from the package root after its build: fetches the published tarball and lists the material differences from the rebuilt `dist/` (sourcemaps excluded, whitespace-only differences ignored). Exit 0 when nothing differs, 3 when something does, 2 when the tarball cannot be read, 64 on usage (no package name, no `dist/`, or `--tarball` with no path). |
|
|
26
26
|
| `scripts/pins.ts` | `--version PRIOR [--range PRIOR_RANGE ...] [--paths src,tests] [--json]`, run from the package root: lists every line under the paths carrying the prior version literal or a prior range literal, once per line, sweeping a one-comparator range such as `^8.3.0` for its bare version in any form. Exit 0 with no hits, 3 with hits, 64 on usage (no literal, or a flag with no value). |
|
|
27
27
|
| `scripts/wave.ts` | `--visit [--target DIR] [--offline] [--from STEP] [--to STEP] [--prior VERSION] [--dry-run] [--json] [--out FILE]` runs one repository's visit in the order [wave.md](references/wave.md) § Visit a repository fixes (pin, commit, overwrite, verify, install, pins, format, gates, compare), stops at the first failing step, and prints each step's exit (a failed step names the `code: message` refusal or the `note` a JSON verb writes on stdout, else the last stderr lines), the bump ruling (the rebuilt dist against the published tarball, and the final runtime dependency set against the published manifest), and a row for `.orkestrel/release.md`; `--prior` names the version a pre-ruled bump replaced so the self-pin sweep finds it; `--plan` prints the packages by layer from the catalog table. Exit 0 when every step passed, 1 when one failed, 2 when a reading failed, 64 on usage. |
|
|
28
|
-
| `scripts/window.ts` | `--whoami [--wait SECONDS]` reads or polls `npm whoami`; `--login` prints the operator's login command and polls `whoami` until it answers; `--publish DIR... --otp CODE` uploads back to back with `npm publish --ignore-scripts --otp`, journals each upload under `tmp/units/`, stops at the first refusal naming where to resume, and confirms each accepted version against the registry; `--confirm NAME@VERSION...` re-reads the registry until it serves the version. Exit 0 on success, 3 when not.
|
|
28
|
+
| `scripts/window.ts` | `--whoami [--wait SECONDS]` reads or polls `npm whoami`; `--login` prints the operator's login command and polls `whoami` until it answers; `--publish DIR... --otp CODE` refuses a batch whose nearest `.git` entry is a file, including ancestor entries, with exit 3 before contacting npm; it names a linked worktree's primary clone or identifies a submodule, then uploads back to back with `npm publish --ignore-scripts --otp`, journals each upload under `tmp/units/`, stops at the first refusal naming where to resume, and confirms each accepted version against the registry; `--confirm NAME@VERSION...` re-reads the registry until it serves the version. Exit 0 on success, 3 when not. |
|
|
29
29
|
|
|
30
30
|
The user's current instruction wins. `references/release.md` owns the credential
|
|
31
31
|
and authorization law; nothing here weakens it.
|
|
@@ -93,6 +93,8 @@ registry copy over any staged tip, so every consumer stays red until that tip is
|
|
|
93
93
|
|
|
94
94
|
## Prepare a layer
|
|
95
95
|
|
|
96
|
+
- Compare release packs between hosts by paths, sizes, and content hashes, and report executable-mode differences separately; a pack made on Windows records `0644` for an entry git tracks as `100755`.
|
|
97
|
+
|
|
96
98
|
An unpublished package's first version is `0.0.1`. Do not bump it before that first publish. The
|
|
97
99
|
registry has nothing to serve, so there is no version to move away from, and bumping produces a
|
|
98
100
|
package whose history starts at a number nothing explains.
|
|
@@ -9,6 +9,8 @@ rest of the layer either fits inside it or takes another approval.
|
|
|
9
9
|
|
|
10
10
|
## Arm the terminal
|
|
11
11
|
|
|
12
|
+
- Before any upload, walk from each package directory to its nearest ancestor carrying a `.git` entry; refuse the batch with exit 3 before contacting npm when that entry is a file. For a linked worktree, resolve its `gitdir:` path and `commondir` file and publish from the named primary clone; for a submodule without `commondir`, publish from a standalone clone.
|
|
13
|
+
|
|
12
14
|
- Read the session with `node .agents/skills/orkestrel-publish/scripts/window.ts --whoami`; `--login` prints the command the operator runs in a real terminal and polls `whoami` until it answers. No child of a script holds the TTY npm needs, so the login itself is the operator's.
|
|
13
15
|
|
|
14
16
|
- Run the login and every browser-authorized publish under `script -qfc '<command>' <log>`. npm
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
// --whoami reads `npm whoami` once, or polls it every 5 seconds until it answers or the wait ends.
|
|
7
7
|
// --login prints the command the operator runs in a real terminal (npm offers its approval only to a
|
|
8
8
|
// TTY, so no child of this script can hold it), then polls `npm whoami` until it answers or the wait
|
|
9
|
-
// ends. --publish
|
|
9
|
+
// ends. --publish refuses linked worktrees and submodules found at a package or its ancestors,
|
|
10
|
+
// and names a linked worktree's primary clone.
|
|
11
|
+
// It then re-reads `npm whoami` immediately before the first upload and refuses when it
|
|
10
12
|
// answers nothing, then runs `npm publish --ignore-scripts --browser=false --otp=CODE` in each
|
|
11
13
|
// directory back to back under captured pipes (npm's non-TTY guard turns a refused code into
|
|
12
14
|
// EOTP with no prompt, so the path needs no TTY), journals each upload to tmp/units/publish-<name>.log, reads the `+ name@version`
|
|
@@ -14,8 +16,8 @@
|
|
|
14
16
|
// each accepted version against the registry. --confirm re-reads the registry for each name@version every 5
|
|
15
17
|
// seconds until it serves the version or the wait (default 120 seconds) ends. Exit 0 when the mode
|
|
16
18
|
// succeeded, 3 when it did not, 64 on usage.
|
|
17
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
18
|
-
import { join } from 'node:path'
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
|
|
20
|
+
import { dirname, join, resolve } from 'node:path'
|
|
19
21
|
import {
|
|
20
22
|
readList,
|
|
21
23
|
readMissingFlags,
|
|
@@ -39,8 +41,8 @@ interface Upload {
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
function waitFor(ms: number): Promise<void> {
|
|
42
|
-
return new Promise((
|
|
43
|
-
setTimeout(
|
|
44
|
+
return new Promise((settle) => {
|
|
45
|
+
setTimeout(settle, ms)
|
|
44
46
|
})
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -196,6 +198,33 @@ async function main(argv: readonly string[]): Promise<number> {
|
|
|
196
198
|
)
|
|
197
199
|
return 64
|
|
198
200
|
}
|
|
201
|
+
for (const directory of directories) {
|
|
202
|
+
let root = resolve(directory)
|
|
203
|
+
while (!existsSync(join(root, '.git')) && dirname(root) !== root) root = dirname(root)
|
|
204
|
+
const entry = join(root, '.git')
|
|
205
|
+
if (!existsSync(entry) || !statSync(entry).isFile()) continue
|
|
206
|
+
const line = readFileSync(entry, 'utf8')
|
|
207
|
+
.split(/\r\n|\n/u)
|
|
208
|
+
.find((value) => value.startsWith('gitdir:'))
|
|
209
|
+
const target = line?.slice('gitdir:'.length).trim()
|
|
210
|
+
if (target === undefined || target === '') {
|
|
211
|
+
console.error(
|
|
212
|
+
`window: ${directory} has a .git file without a gitdir: path; cannot identify its primary clone`,
|
|
213
|
+
)
|
|
214
|
+
return 3
|
|
215
|
+
}
|
|
216
|
+
const gitdir = resolve(root, target)
|
|
217
|
+
const common = join(gitdir, 'commondir')
|
|
218
|
+
if (!existsSync(common)) {
|
|
219
|
+
console.error(`window: ${directory} is a submodule; publish from a standalone clone`)
|
|
220
|
+
return 3
|
|
221
|
+
}
|
|
222
|
+
const primary = dirname(resolve(gitdir, readFileSync(common, 'utf8').trim()))
|
|
223
|
+
console.error(
|
|
224
|
+
`window: ${directory} is a linked worktree; publish from its primary clone at ${primary} so npm records gitHead`,
|
|
225
|
+
)
|
|
226
|
+
return 3
|
|
227
|
+
}
|
|
199
228
|
// A stored credential expires mid-session, so the session-start answer does not hold here.
|
|
200
229
|
if (readWhoami() === undefined) {
|
|
201
230
|
console.error(
|
|
@@ -48,7 +48,7 @@ so network-controlled descriptions never enter agent instruction context.
|
|
|
48
48
|
| `@orkestrel/abort` | `0.0.12` | L1 | `@orkestrel/contract` `^0.0.18` | |
|
|
49
49
|
| `@orkestrel/agent` | `0.0.25` | L5 | `@orkestrel/tool` `^0.0.17`, `@orkestrel/abort` `^0.0.12`, `@orkestrel/queue` `^0.0.15`, `@orkestrel/budget` `^0.0.12`, `@orkestrel/emitter` `^0.0.11`, `@orkestrel/timeout` `^0.0.12`, `@orkestrel/contract` `^0.0.18`, `@orkestrel/database` `^0.0.16`, `@orkestrel/workflow` `^0.0.20`, `@orkestrel/workspace` `^0.0.10` | |
|
|
50
50
|
| `@orkestrel/brief` | `0.0.10` | L4 | `@orkestrel/reason` `^0.0.12`, `@orkestrel/emitter` `^0.0.11`, `@orkestrel/contract` `^0.0.18`, `@orkestrel/interpret` `^0.0.15` | |
|
|
51
|
-
| `@orkestrel/browser` | `0.0.
|
|
51
|
+
| `@orkestrel/browser` | `0.0.21` | L5 | `@orkestrel/mcp` `^0.0.35`, `@orkestrel/html` `^0.0.12`, `@orkestrel/tool` `^0.0.18`, `@orkestrel/router` `^0.0.16`, `@orkestrel/server` `^0.0.22`, `@orkestrel/emitter` `^0.0.11`, `@orkestrel/contract` `^0.0.19`, `@orkestrel/markdown` `^0.0.17`, `@orkestrel/websocket` `^0.0.14` | |
|
|
52
52
|
| `@orkestrel/budget` | `0.0.12` | L1 | `@orkestrel/contract` `^0.0.18` | |
|
|
53
53
|
| `@orkestrel/codec` | `0.0.5` | L1 | `@orkestrel/contract` `^0.0.18` | |
|
|
54
54
|
| `@orkestrel/console` | `0.0.15` | L2 | `@orkestrel/emitter` `^0.0.11`, `@orkestrel/contract` `^0.0.18` | |
|
|
@@ -78,7 +78,7 @@ so network-controlled descriptions never enter agent instruction context.
|
|
|
78
78
|
| `@orkestrel/reason` | `0.0.12` | L2 | `@orkestrel/emitter` `^0.0.11`, `@orkestrel/contract` `^0.0.18` | |
|
|
79
79
|
| `@orkestrel/relation` | `0.0.14` | L3 | `@orkestrel/emitter` `^0.0.11`, `@orkestrel/contract` `^0.0.18`, `@orkestrel/database` `^0.0.16` | |
|
|
80
80
|
| `@orkestrel/router` | `0.0.16` | L2 | `@orkestrel/abort` `^0.0.12`, `@orkestrel/emitter` `^0.0.11`, `@orkestrel/contract` `^0.0.18` | |
|
|
81
|
-
| `@orkestrel/scaffold` | `0.0.
|
|
81
|
+
| `@orkestrel/scaffold` | `0.0.87` | L3 | `@orkestrel/console` `^0.0.15`, `@orkestrel/emitter` `^0.0.11`, `@orkestrel/process` `^0.0.14`, `@orkestrel/contract` `^0.0.19`, `@orkestrel/markdown` `^0.0.17`, `@orkestrel/template` `^0.0.9` | |
|
|
82
82
|
| `@orkestrel/sea` | `0.0.18` | L3 | `@orkestrel/emitter` `^0.0.11`, `@orkestrel/process` `^0.0.14`, `@orkestrel/contract` `^0.0.18` | |
|
|
83
83
|
| `@orkestrel/server` | `0.0.22` | L3 | `@orkestrel/abort` `^0.0.12`, `@orkestrel/codec` `^0.0.5`, `@orkestrel/router` `^0.0.16`, `@orkestrel/emitter` `^0.0.11`, `@orkestrel/timeout` `^0.0.12`, `@orkestrel/contract` `^0.0.19` | |
|
|
84
84
|
| `@orkestrel/sqlite` | `0.0.13` | L1 | `@orkestrel/contract` `^0.0.18` | |
|
|
@@ -133,6 +133,10 @@ neither reads meaning.
|
|
|
133
133
|
`helpers.ts`, `inferers.ts`, `middlewares.ts`, `parsers.ts`, `plugins.ts`, `relations.ts`,
|
|
134
134
|
`schemas.ts`, `seeders.ts`, `shapers.ts`, and `validators.ts`. That list is exhaustive, a new function kind joins it, and no
|
|
135
135
|
later version of the plugin claims more.
|
|
136
|
+
- The name forms read names only: a function, an export specifier, an exported import alias, and a
|
|
137
|
+
star re-export in the kind file. They never read a return type, so a `create{Name}Plugins` that
|
|
138
|
+
returns one plugin passes; review owns arity. A value binding in a kind file is the data rule's
|
|
139
|
+
finding, not the name form's.
|
|
136
140
|
- The plugin reports no `data` violation in `helpers.ts`. The kind rules place a camelCase
|
|
137
141
|
namespace of functions there, and a namespace of callables is not separable from a data table by
|
|
138
142
|
declaration syntax, so `DATA_EXEMPT_FILES` in `configs/policy.ts` excludes the file. Ordinary
|
|
@@ -292,6 +292,8 @@ Use the real browser as the system under test:
|
|
|
292
292
|
|
|
293
293
|
- Do not replace DOM events, storage, observers, viewports, layout methods, pointer, or drag APIs unless the browser genuinely lacks one.
|
|
294
294
|
- Prefer real nodes, events, styles/layout, and observers.
|
|
295
|
+
- In a file compiled by a browser or Vue scope, never load `@vitest/browser-playwright` types; read `cdp()` as `unknown`, and narrow `send` and each reply with guards.
|
|
296
|
+
- When a browser test moves the real pointer through CDP, move it to `(-1, -1)` in the case's `finally` block.
|
|
295
297
|
- Centralize event factories: `createPointerEvent`, `createDragEvent`, `typeInput`, `fireTransitionEnd`.
|
|
296
298
|
- Centralize DOM builders: `createButtonElement`, `createDropdownElements`, `createModalElement`.
|
|
297
299
|
- Assert DOM state, emitted events, callback records, focus, classes, attributes, and public API state.
|
|
@@ -242,7 +242,7 @@ then runs the configured scoped checks that prove environment isolation.
|
|
|
242
242
|
| Scope | `lib` | `types` | Permitted host globals |
|
|
243
243
|
| ---------------------------- | --------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
244
244
|
| `src:core`, `app:core` | `["ESNext","WebWorker"]` | `[]` | WHATWG web interop: fetch family, streams, URL, Abort, encoders, crypto, timers, console, DOMException, structuredClone; no DOM, no Node |
|
|
245
|
-
| `src:browser`, `app:browser` | `["ESNext","DOM","DOM.Iterable"]` |
|
|
245
|
+
| `src:browser`, `app:browser` | `["ESNext","DOM","DOM.Iterable"]` | `["vite/client"]` | DOM; no Node |
|
|
246
246
|
| `src:server`, `app:server` | `["ESNext"]` | `["node"]` | Node; no DOM |
|
|
247
247
|
| `src:styles`, `src:<name>` | `["ESNext"]` | `["vite/client"]` | Vite SCSS module declaration only |
|
|
248
248
|
| `src:vue` | `["ESNext","DOM","DOM.Iterable"]` | `["vite/client"]` | DOM; no Node |
|
|
@@ -287,7 +287,10 @@ export const POLICY_URL_PATTERN = /https?:\/\/\S+/gu
|
|
|
287
287
|
export const POLICY_NAME_FORMS: Readonly<Record<string, PolicyNameForm>> = Object.freeze({
|
|
288
288
|
'parsers.ts': Object.freeze({ pattern: /^parse/u, messageId: 'parser' }),
|
|
289
289
|
'factories.ts': Object.freeze({ pattern: /^create/u, messageId: 'factory' }),
|
|
290
|
-
'plugins.ts': Object.freeze({
|
|
290
|
+
'plugins.ts': Object.freeze({
|
|
291
|
+
pattern: /^create[A-Z][A-Za-z0-9]*Plugins?$/u,
|
|
292
|
+
messageId: 'plugin',
|
|
293
|
+
}),
|
|
291
294
|
})
|
|
292
295
|
|
|
293
296
|
/**
|
|
@@ -1010,8 +1013,8 @@ export function reportConstant(context: PolicyContext, node: PolicyExpression):
|
|
|
1010
1013
|
|
|
1011
1014
|
/**
|
|
1012
1015
|
* Reports a function in the named kind file whose name breaks that file's form, an export specifier
|
|
1013
|
-
* there that publishes a name outside it, and a star re-export there, whose
|
|
1014
|
-
* read.
|
|
1016
|
+
* or exported import alias there that publishes a name outside it, and a star re-export there, whose
|
|
1017
|
+
* names the form cannot read.
|
|
1015
1018
|
*/
|
|
1016
1019
|
export function reportName(context: PolicyContext, node: PolicyExpression, file: string): void {
|
|
1017
1020
|
const form = POLICY_NAME_FORMS[file]
|
|
@@ -1021,6 +1024,13 @@ export function reportName(context: PolicyContext, node: PolicyExpression, file:
|
|
|
1021
1024
|
return
|
|
1022
1025
|
}
|
|
1023
1026
|
if (node.type === 'ExportNamedDeclaration') {
|
|
1027
|
+
const alias = node.declaration
|
|
1028
|
+
if (alias?.type === 'TSImportEqualsDeclaration') {
|
|
1029
|
+
const name = identifierToPolicyName(alias.id)
|
|
1030
|
+
if (name === undefined || !form.pattern.test(name)) {
|
|
1031
|
+
context.report({ node: alias, messageId: form.messageId })
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1024
1034
|
for (const specifier of node.specifiers ?? []) {
|
|
1025
1035
|
const exported = specifier.exported
|
|
1026
1036
|
const name =
|