@khanglvm/outline-cli 0.1.1

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.
@@ -0,0 +1,2 @@
1
+ OUTLINE_TEST_BASE_URL=https://example.getoutline.site
2
+ OUTLINE_TEST_API_KEY=ol_api_replace_me
package/AGENTS.md ADDED
@@ -0,0 +1,107 @@
1
+ # AGENTS.md
2
+
3
+ ## Scope
4
+ This repository contains `outline-cli` (`outline-agent` alias), a Node.js CLI optimized for AI agents to interact with Outline via real API calls.
5
+
6
+ ## Core Principles
7
+ - Keep outputs deterministic and machine-readable.
8
+ - Optimize for low-token workflows (`summary/ids` views first, hydrate later).
9
+ - Prefer batch operations for fewer round trips.
10
+ - Never mutate existing workspace content in tests except suite-created test documents.
11
+ - Keep security strict: no hardcoded live secrets in tracked files.
12
+
13
+ ## Runtime + Commands
14
+ - Node.js: `>=18.17`
15
+ - Install: `npm install`
16
+ - Basic check: `npm run check`
17
+ - Full tests (real environment): `npm test`
18
+
19
+ ## Repository Map
20
+ - CLI entrypoint: `bin/outline-cli.js`
21
+ - Command wiring/output modes: `src/cli.js`
22
+ - API client/auth/retry: `src/outline-client.js`
23
+ - Tool registry/core tools: `src/tools.js`
24
+ - Navigation/search tools: `src/tools.navigation.js`
25
+ - Mutation/revision tools: `src/tools.mutation.js`
26
+ - Platform/cleanup/capabilities tools: `src/tools.platform.js`
27
+ - Tool arg validation: `src/tool-arg-schemas.js`
28
+ - Live integration tests: `test/live.integration.test.js`
29
+
30
+ ## Local Environment
31
+ - Template: `.env.test.example`
32
+ - Local secret file (untracked): `.env.test.local`
33
+ - Required vars for live tests:
34
+ - `OUTLINE_TEST_BASE_URL`
35
+ - `OUTLINE_TEST_API_KEY`
36
+
37
+ ## Development Workflow
38
+ 1. Pull latest branch and inspect current tool contracts:
39
+ - `node ./bin/outline-cli.js tools contract all --result-mode inline`
40
+ 2. Refresh raw API method inventory from prior sessions / wrappers, then diff wrapped vs raw:
41
+ - `rg -o 'client\\.call\\(\"[^\"]+\"' src | sed -E 's/.*\\(\"//; s/\"$//' | sort | uniq`
42
+ - record missing high-value endpoints in `/tmp/knowledges/outline-raw-api-gap.md`
43
+ 3. Implement minimal, compatible changes (preserve response envelopes).
44
+ 4. Add/adjust arg schema in `src/tool-arg-schemas.js` for every new tool arg.
45
+ 5. Add/adjust real integration tests in `test/live.integration.test.js`.
46
+ 6. Run:
47
+ - `npm run check`
48
+ - `npm test`
49
+ 7. Update docs when behavior/signature changes:
50
+ - `README.md`
51
+ - `docs/TOOL_CONTRACTS.md`
52
+
53
+ ## Testing Rules (Live Env)
54
+ - No mocks when endpoint can be exercised live.
55
+ - Mutation tests must:
56
+ - create a dedicated test doc first,
57
+ - perform all edits/patch/revision operations on that doc,
58
+ - delete it in cleanup.
59
+ - Read-only tools (search/list/info) may use site-wide data.
60
+ - Keep tests resilient: isolate steps with subtests and clear assertions.
61
+
62
+ ## Tool/Output Requirements
63
+ - Default JSON output remains stable.
64
+ - `--output ndjson` must stay stream-friendly and compatible with file-offload behavior.
65
+ - Large responses should offload to temp files when `result-mode` is `auto|file`.
66
+ - `batch` should default to token-efficient item payloads; full envelopes opt-in.
67
+
68
+ ## Agent Action Gate
69
+ - Mutating actions are gated by default and must be explicit:
70
+ - pass `performAction: true` on mutation/delete operations.
71
+ - Safe delete flow requires prior read confirmation:
72
+ 1. Call `documents.info` with `armDelete: true` on target document(s).
73
+ 2. Use returned `deleteReadReceipt.token` as `readToken` for delete.
74
+ 3. Execute delete only with `performAction: true`.
75
+ - Delete operations must fail if read token is missing, stale, mismatched, or expired.
76
+
77
+ ## Security + Secrets
78
+ - Never commit real Outline API keys or `.env.test.local`.
79
+ - Secret scan guard test must pass.
80
+ - If a key is exposed, rotate immediately and update local env.
81
+
82
+ ## Deployment / Release
83
+ - Pre-release checklist:
84
+ 1. Ensure clean git working tree.
85
+ 2. Ensure `OUTLINE_ENTRY_BUILD_KEY` is set (`.env.local` or env).
86
+ 3. Ensure npm auth is ready (`npm login`).
87
+ - Primary workflow command:
88
+ - `npm run release -- --bump patch`
89
+ - or `npm run release -- --version X.Y.Z`
90
+ - Prepare-only workflow (no publish/push):
91
+ - `npm run release:prepare -- --bump patch`
92
+ - Agent execution rule:
93
+ - If user asks to "deploy" or "release", run `npm run release -- --bump patch` by default unless user specifies a version/tag strategy.
94
+ - Release script responsibilities (`scripts/release.mjs`):
95
+ - version bump (`npm version --no-git-tag-version`)
96
+ - changelog update (`CHANGELOG.md`)
97
+ - integrity refresh (`npm run integrity:refresh`)
98
+ - verification (`npm run check`, `npm test`)
99
+ - packaging validation (`npm pack --dry-run`)
100
+ - git commit + tag (`chore(release): vX.Y.Z`, `vX.Y.Z`)
101
+ - npm publish (`--access public`) and git push to `origin`
102
+
103
+ ## Temporary Knowledge Notes
104
+ Use local scratch references under:
105
+ - `/tmp/knowledges/*.md`
106
+
107
+ These are intentionally ignored by git and can store short-lived research/decision notes.
package/CHANGELOG.md ADDED
@@ -0,0 +1,102 @@
1
+ # Changelog
2
+
3
+ ## 0.1.1 - 2026-03-05
4
+
5
+ - Initial tagged release notes.
6
+ - feat(help): add ai skill guidance section for scenario workflows (81a6368)
7
+ - feat(uc19): add compliance oauth tests and docs (b97ab28)
8
+ - feat(uc19): add oauth client and auth wrappers (c1c31f8)
9
+ - feat(uc14): add permanent delete docs and tests (c336bb8)
10
+ - feat(uc14): add permanent delete wrapper (fa99d2e)
11
+ - feat(uc09): add apply_patch_safe tests and docs (41078ae)
12
+ - feat(uc09): add apply_patch_safe wrapper (bc87f7e)
13
+ - feat(uc07): add issue ref helper tests and docs (3254bf5)
14
+ - feat(uc07): add issue reference helper tools (ccf09bc)
15
+ - feat(uc13): add workspace automation tests and docs (75776b4)
16
+ - feat(uc13): add user lifecycle automation wrappers (877adbe)
17
+ - feat(uc12): add migration scenario tests and docs (db33d85)
18
+ - feat(uc12): add import and file operation primitives (25316c5)
19
+ - feat(uc11): add template pipeline tests and docs (c3b126d)
20
+ - feat(uc11): add placeholder pipeline tools (e5fe193)
21
+ - feat(uc10): add graph scenario tests and docs (e2f497a)
22
+ - feat(uc10): add graph traversal helpers (33432d9)
23
+ - feat(uc09): add rollback-safety scenario tests and docs (7bc947c)
24
+ - feat(uc09): add revisions diff helper (d94511e)
25
+ - feat(uc07): add issue-link scenario tests and docs (508281f)
26
+ - feat(uc07): add data-attribute wrappers and schema alignment (cec94ce)
27
+ - feat(uc06): add role-visibility scenario tests and docs (9f2b2ae)
28
+ - feat(uc06): harden role and membership contracts (76d33d7)
29
+ - feat(uc05): add sharing scenario tests and docs (fbc35a2)
30
+ - feat(uc05): harden share lifecycle contracts (6f594f0)
31
+ - feat(uc04): add internal-faq scenario coverage and docs (7cbb697)
32
+ - feat(uc03): add meeting-notes scenario coverage and docs (4f4fb0f)
33
+ - chore: checkpoint local outline-cli changes (9e75cd9)
34
+ - fix(validation): validate expectedRevision in documents.apply_patch (e51fb8b)
35
+ - test(schema): add coverage for federated and terminology tools (b925c48)
36
+ - feat(workflows): add federated and terminology composite tools (5b3fa36)
37
+ - test(schema): cover new scenario wrappers and safety rules (35401fb)
38
+ - feat(tools): add scenario-critical wrappers and safety hardening (d4d9fac)
39
+ - docs(usecases): improve UC-20 issue traceability (111f4fd)
40
+ - docs(usecases): improve UC-19 issue traceability (7dc1278)
41
+ - docs(usecases): improve UC-18 issue traceability (8d594f4)
42
+ - docs(usecases): improve UC-17 issue traceability (2da81d5)
43
+ - docs(usecases): improve UC-16 issue traceability (df28cb2)
44
+ - docs(usecases): improve UC-15 issue traceability (d831dac)
45
+ - docs(usecases): improve UC-14 issue traceability (9947f95)
46
+ - docs(usecases): improve UC-13 issue traceability (ecd07c7)
47
+ - docs(usecases): improve UC-12 issue traceability (7d3dfdc)
48
+ - docs(usecases): improve UC-11 issue traceability (1155797)
49
+ - docs(usecases): improve UC-10 issue traceability (8770670)
50
+ - docs(usecases): improve UC-09 issue traceability (01733dd)
51
+ - docs(usecases): improve UC-08 issue traceability (11b7892)
52
+ - docs(usecases): improve UC-07 issue traceability (66fd433)
53
+ - docs(usecases): improve UC-06 issue traceability (f769da8)
54
+ - docs(usecases): improve UC-05 issue traceability (0dc78b5)
55
+ - docs(usecases): improve UC-04 issue traceability (d8afaf4)
56
+ - docs(usecases): improve UC-03 issue traceability (b8ac03e)
57
+ - docs(usecases): improve UC-02 issue traceability (6305567)
58
+ - docs(usecases): improve UC-01 issue traceability (e3b3459)
59
+ - docs(usecases): add index for UC scenario artifacts (32ce730)
60
+ - docs(usecases): add UC-20 secure lifecycle governance improvement (3a5abe0)
61
+ - docs(usecases): add UC-19 compliance access auditability improvement (39a6843)
62
+ - docs(usecases): add UC-18 terminology refactor improvement (64e4df1)
63
+ - docs(usecases): add UC-17 editorial review lifecycle improvement (692cefa)
64
+ - docs(usecases): add UC-16 federated search sync improvement (b3bad9a)
65
+ - docs(usecases): add UC-15 ai semantic qa improvement (6cd384e)
66
+ - docs(usecases): add UC-14 webhook workflow improvement (9cadbea)
67
+ - docs(usecases): add UC-13 api driven workspace automation improvement (c80a6a9)
68
+ - docs(usecases): add UC-12 legacy wiki migration improvement (6c00a56)
69
+ - docs(usecases): add UC-11 template driven doc pipeline improvement (39a492b)
70
+ - docs(usecases): add UC-10 crosslinked knowledge graph improvement (4a90748)
71
+ - docs(usecases): add UC-09 postmortem rca rollback safety improvement (d6a02dd)
72
+ - docs(usecases): add UC-08 runbooks incident playbooks improvement (48df335)
73
+ - docs(usecases): add UC-07 project docs issue linkage improvement (c7804bb)
74
+ - docs(usecases): add UC-06 role based department spaces improvement (f3f14fe)
75
+ - docs(usecases): add UC-05 public docs sharing improvement (5f56b43)
76
+ - docs(usecases): add UC-04 internal faq knowledge base improvement (1d3c1c9)
77
+ - docs(usecases): add UC-03 meeting notes decision logs improvement (9a115d3)
78
+ - docs(usecases): add UC-02 sop policy wiki improvement (f34462e)
79
+ - docs(usecases): add UC-01 handbook onboarding improvement (1d41ab0)
80
+ - wip (5dae3ea)
81
+
82
+ All notable changes to this project are documented in this file.
83
+
84
+ The format follows Keep a Changelog principles and this project uses Semantic Versioning.
85
+
86
+ ## Unreleased
87
+
88
+ ### Changed
89
+
90
+ - Improved top-level documentation with a user-focused quickstart and clearer operational guidance.
91
+
92
+ ### Added
93
+
94
+ - Added a compact AI-agent instruction section in `README.md` for deterministic low-token workflows.
95
+
96
+ ## 0.1.0 - 2026-03-05
97
+
98
+ ### Added
99
+
100
+ - Initial release baseline for `@khanglvm/outline-cli`.
101
+ - Agent-optimized Outline CLI with deterministic JSON/NDJSON outputs, profile management, and batch invocation.
102
+ - Mutation safety gates (`performAction`) and safe delete flow with read-token confirmation.
package/README.md ADDED
@@ -0,0 +1,244 @@
1
+ # outline-cli
2
+
3
+ `outline-cli` (alias `outline-agent`) is a Node.js CLI for the Outline API, designed for both human operators and AI agents.
4
+
5
+ It prioritizes deterministic, machine-friendly output:
6
+
7
+ - Stable JSON envelopes
8
+ - Token-efficient `ids` and `summary` views
9
+ - Batch operations to reduce API round trips
10
+ - Safe mutation gates (`performAction: true`)
11
+ - Automatic large-result offload to temp files
12
+
13
+ ## Quick Start
14
+
15
+ Prerequisites:
16
+
17
+ - Node.js `>=18.17`
18
+ - An Outline workspace URL
19
+ - An Outline API key (recommended auth mode)
20
+
21
+ Run from npm without installing:
22
+
23
+ ```bash
24
+ npx @khanglvm/outline-cli --help
25
+ ```
26
+
27
+ Or install locally in this repo:
28
+
29
+ ```bash
30
+ npm install
31
+ node ./bin/outline-cli.js --help
32
+ ```
33
+
34
+ Set up a profile (API key mode):
35
+
36
+ ```bash
37
+ npx @khanglvm/outline-cli profile add prod \
38
+ --base-url https://app.getoutline.com \
39
+ --api-key "$OUTLINE_API_KEY" \
40
+ --set-default
41
+ ```
42
+
43
+ Verify auth:
44
+
45
+ ```bash
46
+ npx @khanglvm/outline-cli profile test prod --pretty
47
+ ```
48
+
49
+ Run your first search:
50
+
51
+ ```bash
52
+ npx @khanglvm/outline-cli invoke documents.search \
53
+ --args '{"query":"oncall runbook","mode":"semantic","limit":5,"view":"summary"}' \
54
+ --pretty
55
+ ```
56
+
57
+ ## Day-to-Day Usage
58
+
59
+ Discover tools and contracts:
60
+
61
+ ```bash
62
+ npx @khanglvm/outline-cli tools list
63
+ npx @khanglvm/outline-cli tools contract documents.search --pretty
64
+ npx @khanglvm/outline-cli tools contract all --result-mode inline
65
+ ```
66
+
67
+ Read document metadata by id:
68
+
69
+ ```bash
70
+ npx @khanglvm/outline-cli invoke documents.info \
71
+ --args '{"id":"<document-id>","view":"summary"}'
72
+ ```
73
+
74
+ Create a document:
75
+
76
+ ```bash
77
+ npx @khanglvm/outline-cli invoke documents.create \
78
+ --args '{"title":"Release Notes","text":"# Release Notes","publish":false,"view":"summary"}'
79
+ ```
80
+
81
+ Update a document (mutation requires `performAction: true`):
82
+
83
+ ```bash
84
+ npx @khanglvm/outline-cli invoke documents.update \
85
+ --args '{"id":"<document-id>","text":"\n\nUpdated by automation.","editMode":"append","performAction":true,"view":"summary"}'
86
+ ```
87
+
88
+ Batch multiple calls:
89
+
90
+ ```bash
91
+ npx @khanglvm/outline-cli batch --ops '[
92
+ {"tool":"collections.list","args":{"limit":5,"view":"summary"}},
93
+ {"tool":"documents.search","args":{"query":"incident","limit":5,"view":"ids"}}
94
+ ]'
95
+ ```
96
+
97
+ ## Safe Delete Flow
98
+
99
+ Delete is guarded by read-token confirmation.
100
+
101
+ 1. Arm-delete read:
102
+
103
+ ```bash
104
+ npx @khanglvm/outline-cli invoke documents.info \
105
+ --args '{"id":"<document-id>","armDelete":true,"view":"summary"}'
106
+ ```
107
+
108
+ 2. Copy the returned `deleteReadReceipt.token`, then delete:
109
+
110
+ ```bash
111
+ npx @khanglvm/outline-cli invoke documents.delete \
112
+ --args '{"id":"<document-id>","readToken":"<token>","performAction":true}'
113
+ ```
114
+
115
+ ## Output Modes and Temp Files
116
+
117
+ Output format:
118
+
119
+ - `--output json` (default)
120
+ - `--output ndjson` for stream-friendly parsing
121
+
122
+ Result mode:
123
+
124
+ - `--result-mode auto` (default): inline until payload is too large
125
+ - `--result-mode inline`: always inline JSON
126
+ - `--result-mode file`: always write to temp file and return file pointer
127
+
128
+ Temp-file management:
129
+
130
+ ```bash
131
+ npx @khanglvm/outline-cli tmp list
132
+ npx @khanglvm/outline-cli tmp cat /absolute/path/from/result.json
133
+ npx @khanglvm/outline-cli tmp gc --older-than-hours 24
134
+ ```
135
+
136
+ ## Profile Management
137
+
138
+ Add password-mode profile:
139
+
140
+ ```bash
141
+ npx @khanglvm/outline-cli profile add internal \
142
+ --base-url https://outline.company.com \
143
+ --auth-type password \
144
+ --username agent@company.com \
145
+ --password "$OUTLINE_PASSWORD"
146
+ ```
147
+
148
+ Select default profile:
149
+
150
+ ```bash
151
+ npx @khanglvm/outline-cli profile use prod
152
+ ```
153
+
154
+ Improve AI profile routing metadata:
155
+
156
+ ```bash
157
+ npx @khanglvm/outline-cli profile annotate prod \
158
+ --description "Production knowledge base" \
159
+ --append-keywords "prod,runbook,incident"
160
+
161
+ npx @khanglvm/outline-cli profile enrich prod \
162
+ --query "incident escalation process" \
163
+ --titles "Incident Playbook,Escalation Matrix"
164
+ ```
165
+
166
+ ## AI Agent Mini Instructions
167
+
168
+ Use this short operating pattern when an AI agent drives the CLI:
169
+
170
+ 1. Start with `tools contract all --result-mode inline`.
171
+ 2. Prefer `view:"ids"` or `view:"summary"` first; hydrate to `full` only when needed.
172
+ 3. Bundle independent reads into one `batch` call.
173
+ 4. For any mutation endpoint, explicitly set `performAction:true`.
174
+ 5. For delete, always run `documents.info` with `armDelete:true` first and pass the returned read token.
175
+ 6. If output is file-offloaded, read only the required fields via `tmp cat` + `jq`.
176
+
177
+ For structured AI playbooks and scenario guides:
178
+
179
+ ```bash
180
+ npx @khanglvm/outline-cli tools help ai-skills --view summary
181
+ npx @khanglvm/outline-cli tools help ai-skills --scenario UC-12
182
+ ```
183
+
184
+ ## Testing (Live Environment)
185
+
186
+ Set test credentials in local env file:
187
+
188
+ ```bash
189
+ cp .env.test.example .env.test.local
190
+ # set OUTLINE_TEST_BASE_URL and OUTLINE_TEST_API_KEY
191
+ ```
192
+
193
+ Run checks:
194
+
195
+ ```bash
196
+ npm run check
197
+ npm test
198
+ ```
199
+
200
+ Test rule in this repository:
201
+
202
+ - Mutation tests create and clean up their own test documents.
203
+ - Read-only tests may use site-wide data.
204
+
205
+ ## Release and Publish
206
+
207
+ Standard release flow:
208
+
209
+ ```bash
210
+ npm run release -- --bump patch
211
+ ```
212
+
213
+ This flow performs:
214
+
215
+ - Version bump
216
+ - `CHANGELOG.md` update
217
+ - Integrity refresh (`npm run integrity:refresh`)
218
+ - Verification (`npm run check`, `npm test`)
219
+ - `npm publish --access public`
220
+ - Git commit, tag, and push to `origin`
221
+
222
+ Prepare without publishing/pushing:
223
+
224
+ ```bash
225
+ npm run release:prepare -- --bump patch
226
+ ```
227
+
228
+ Release prerequisites:
229
+
230
+ - Clean working tree (unless you intentionally pass `--allow-dirty`)
231
+ - `OUTLINE_ENTRY_BUILD_KEY` available in environment or `.env.local`
232
+ - npm auth ready (`npm login`)
233
+
234
+ ## Security Notes
235
+
236
+ - Never commit real API keys.
237
+ - Keep local secrets in untracked files such as `.env.test.local`.
238
+ - Profile secrets are stored in OS keychain by default.
239
+
240
+ ## Reference Docs
241
+
242
+ - Tool contracts: [`docs/TOOL_CONTRACTS.md`](docs/TOOL_CONTRACTS.md)
243
+ - Agent rules for this repo: [`AGENTS.md`](AGENTS.md)
244
+ - Release script: [`scripts/release.mjs`](scripts/release.mjs)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import("./outline-cli.js").catch((err) => {
3
+ process.stderr.write(`${err?.stack || err}\n`);
4
+ process.exit(1);
5
+ });
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import { assertEntryIntegrity } from "../src/entry-integrity.js";
3
+
4
+ async function main() {
5
+ await assertEntryIntegrity();
6
+ const { run } = await import("../src/cli.js");
7
+ await run(process.argv);
8
+ }
9
+
10
+ main().catch((err) => {
11
+ process.stderr.write(`${err?.stack || err}\n`);
12
+ process.exit(1);
13
+ });
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@khanglvm/outline-cli",
3
+ "version": "0.1.1",
4
+ "description": "Agent-optimized CLI for Outline API",
5
+ "type": "module",
6
+ "bin": {
7
+ "outline-cli": "bin/outline-cli.js",
8
+ "outline-agent": "bin/outline-cli.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node ./bin/outline-cli.js",
12
+ "check": "node ./bin/outline-cli.js --help",
13
+ "test": "node --test ./test/**/*.test.js",
14
+ "integrity:refresh": "node ./scripts/generate-entry-integrity.mjs",
15
+ "release": "node ./scripts/release.mjs",
16
+ "release:prepare": "node ./scripts/release.mjs --no-publish --no-push"
17
+ },
18
+ "engines": {
19
+ "node": ">=18.17"
20
+ },
21
+ "dependencies": {
22
+ "@napi-rs/keyring": "^1.2.0",
23
+ "commander": "^13.1.0"
24
+ }
25
+ }
@@ -0,0 +1,123 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const repoRoot = path.resolve(__dirname, "..");
8
+
9
+ const bindingFile = path.join(repoRoot, "src", "entry-integrity-binding.generated.js");
10
+ const manifestFile = path.join(repoRoot, "src", "entry-integrity-manifest.generated.js");
11
+
12
+ const excludedFiles = new Set([
13
+ "src/entry-integrity-binding.generated.js",
14
+ "src/entry-integrity-manifest.generated.js",
15
+ ]);
16
+
17
+ function digestHex(value) {
18
+ return crypto.createHash("sha256").update(value).digest("hex");
19
+ }
20
+
21
+ function canonicalManifest(files) {
22
+ return files
23
+ .map((item) => ({
24
+ path: item.path.replace(/\\/g, "/"),
25
+ sha256: item.sha256,
26
+ }))
27
+ .sort((a, b) => a.path.localeCompare(b.path))
28
+ .map((item) => `${item.path}:${item.sha256}`)
29
+ .join("\n");
30
+ }
31
+
32
+ async function walk(dirPath) {
33
+ const output = [];
34
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
35
+ entries.sort((a, b) => a.name.localeCompare(b.name));
36
+ for (const entry of entries) {
37
+ const abs = path.join(dirPath, entry.name);
38
+ if (entry.isDirectory()) {
39
+ output.push(...(await walk(abs)));
40
+ continue;
41
+ }
42
+ const rel = path.relative(repoRoot, abs).replace(/\\/g, "/");
43
+ if (!rel.endsWith(".js")) {
44
+ continue;
45
+ }
46
+ if (excludedFiles.has(rel)) {
47
+ continue;
48
+ }
49
+ output.push(rel);
50
+ }
51
+ return output;
52
+ }
53
+
54
+ async function hashFiles(files) {
55
+ const output = [];
56
+ for (const rel of files) {
57
+ const abs = path.join(repoRoot, rel);
58
+ const raw = await fs.readFile(abs);
59
+ output.push({
60
+ path: rel,
61
+ sha256: digestHex(raw),
62
+ });
63
+ }
64
+ return output;
65
+ }
66
+
67
+ function toBindingSource({ keySalt, keyId }) {
68
+ return `// Auto-generated by scripts/generate-entry-integrity.mjs
69
+ export const ENTRY_INTEGRITY_BINDING = Object.freeze({
70
+ algorithm: "sha256-salted-manifest-v1",
71
+ keyId: ${JSON.stringify(keyId)},
72
+ keySalt: ${JSON.stringify(keySalt)},
73
+ });
74
+ `;
75
+ }
76
+
77
+ function toManifestSource({ generatedAt, signature, files }) {
78
+ return `// Auto-generated by scripts/generate-entry-integrity.mjs
79
+ export const ENTRY_INTEGRITY_MANIFEST = Object.freeze({
80
+ version: 1,
81
+ algorithm: "sha256",
82
+ signatureAlgorithm: "sha256-salted-manifest-v1",
83
+ signature: ${JSON.stringify(signature)},
84
+ generatedAt: ${JSON.stringify(generatedAt)},
85
+ files: ${JSON.stringify(files, null, 2)},
86
+ });
87
+ `;
88
+ }
89
+
90
+ async function main() {
91
+ const buildKey = process.env.OUTLINE_ENTRY_BUILD_KEY || "dev-local-entry-integrity-key";
92
+ const keySalt = digestHex(buildKey);
93
+ const keyId = keySalt.slice(0, 12);
94
+
95
+ const srcFiles = await walk(path.join(repoRoot, "src"));
96
+ const hashes = await hashFiles(srcFiles);
97
+ const signature = digestHex(`${keySalt}\n${canonicalManifest(hashes)}`);
98
+
99
+ const generatedAt = new Date().toISOString();
100
+ await fs.writeFile(bindingFile, toBindingSource({ keySalt, keyId }), "utf8");
101
+ await fs.writeFile(
102
+ manifestFile,
103
+ toManifestSource({
104
+ generatedAt,
105
+ signature,
106
+ files: hashes,
107
+ }),
108
+ "utf8"
109
+ );
110
+
111
+ process.stdout.write(
112
+ `${JSON.stringify({
113
+ ok: true,
114
+ files: hashes.length,
115
+ manifestFile: path.relative(repoRoot, manifestFile),
116
+ bindingFile: path.relative(repoRoot, bindingFile),
117
+ keyId,
118
+ generatedAt,
119
+ }, null, 2)}\n`
120
+ );
121
+ }
122
+
123
+ await main();