orient-cli 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/extensions/orient-playwright.d.ts +7 -0
  2. package/dist/extensions/orient-playwright.js +8 -0
  3. package/dist/extensions/orient-playwright.js.map +1 -0
  4. package/dist/extensions/orient-web-search.d.ts +5 -0
  5. package/dist/extensions/orient-web-search.js +6 -0
  6. package/dist/extensions/orient-web-search.js.map +1 -0
  7. package/dist/integrations/notebooklm/setup.d.ts +94 -0
  8. package/dist/integrations/notebooklm/setup.js +311 -0
  9. package/dist/integrations/notebooklm/setup.js.map +1 -0
  10. package/dist/integrations/playwright/bridge.d.ts +85 -0
  11. package/dist/integrations/playwright/bridge.js +225 -0
  12. package/dist/integrations/playwright/bridge.js.map +1 -0
  13. package/dist/integrations/playwright/playwright-extension.d.ts +22 -0
  14. package/dist/integrations/playwright/playwright-extension.js +209 -0
  15. package/dist/integrations/playwright/playwright-extension.js.map +1 -0
  16. package/dist/integrations/web-search/bridge.d.ts +79 -0
  17. package/dist/integrations/web-search/bridge.js +298 -0
  18. package/dist/integrations/web-search/bridge.js.map +1 -0
  19. package/dist/integrations/web-search/web-search-extension.d.ts +26 -0
  20. package/dist/integrations/web-search/web-search-extension.js +122 -0
  21. package/dist/integrations/web-search/web-search-extension.js.map +1 -0
  22. package/dist/orient/orient-extension.js +149 -1
  23. package/dist/orient/orient-extension.js.map +1 -1
  24. package/dist/package-paths.js +6 -1
  25. package/dist/package-paths.js.map +1 -1
  26. package/node_modules/@orient-cli/agent-core/package.json +2 -2
  27. package/node_modules/@orient-cli/ai/package.json +1 -1
  28. package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  29. package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.js +2 -2
  30. package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  31. package/node_modules/@orient-cli/coding-agent/package.json +4 -4
  32. package/node_modules/@orient-cli/tui/package.json +1 -1
  33. package/node_modules/mime-db/HISTORY.md +541 -0
  34. package/node_modules/mime-db/LICENSE +23 -0
  35. package/node_modules/mime-db/README.md +109 -0
  36. package/node_modules/mime-db/db.json +9342 -0
  37. package/node_modules/mime-db/index.js +12 -0
  38. package/node_modules/mime-db/package.json +56 -0
  39. package/node_modules/mime-types/HISTORY.md +428 -0
  40. package/node_modules/mime-types/LICENSE +23 -0
  41. package/node_modules/mime-types/README.md +126 -0
  42. package/node_modules/mime-types/index.js +211 -0
  43. package/node_modules/mime-types/mimeScore.js +57 -0
  44. package/node_modules/mime-types/package.json +49 -0
  45. package/package.json +13 -6
  46. package/scripts/postinstall.mjs +235 -0
@@ -0,0 +1,211 @@
1
+ /*!
2
+ * mime-types
3
+ * Copyright(c) 2014 Jonathan Ong
4
+ * Copyright(c) 2015 Douglas Christopher Wilson
5
+ * MIT Licensed
6
+ */
7
+
8
+ 'use strict'
9
+
10
+ /**
11
+ * Module dependencies.
12
+ * @private
13
+ */
14
+
15
+ var db = require('mime-db')
16
+ var extname = require('path').extname
17
+ var mimeScore = require('./mimeScore')
18
+
19
+ /**
20
+ * Module variables.
21
+ * @private
22
+ */
23
+
24
+ var EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/
25
+ var TEXT_TYPE_REGEXP = /^text\//i
26
+
27
+ /**
28
+ * Module exports.
29
+ * @public
30
+ */
31
+
32
+ exports.charset = charset
33
+ exports.charsets = { lookup: charset }
34
+ exports.contentType = contentType
35
+ exports.extension = extension
36
+ exports.extensions = Object.create(null)
37
+ exports.lookup = lookup
38
+ exports.types = Object.create(null)
39
+ exports._extensionConflicts = []
40
+
41
+ // Populate the extensions/types maps
42
+ populateMaps(exports.extensions, exports.types)
43
+
44
+ /**
45
+ * Get the default charset for a MIME type.
46
+ *
47
+ * @param {string} type
48
+ * @return {false|string}
49
+ */
50
+
51
+ function charset (type) {
52
+ if (!type || typeof type !== 'string') {
53
+ return false
54
+ }
55
+
56
+ // TODO: use media-typer
57
+ var match = EXTRACT_TYPE_REGEXP.exec(type)
58
+ var mime = match && db[match[1].toLowerCase()]
59
+
60
+ if (mime && mime.charset) {
61
+ return mime.charset
62
+ }
63
+
64
+ // default text/* to utf-8
65
+ if (match && TEXT_TYPE_REGEXP.test(match[1])) {
66
+ return 'UTF-8'
67
+ }
68
+
69
+ return false
70
+ }
71
+
72
+ /**
73
+ * Create a full Content-Type header given a MIME type or extension.
74
+ *
75
+ * @param {string} str
76
+ * @return {false|string}
77
+ */
78
+
79
+ function contentType (str) {
80
+ // TODO: should this even be in this module?
81
+ if (!str || typeof str !== 'string') {
82
+ return false
83
+ }
84
+
85
+ var mime = str.indexOf('/') === -1 ? exports.lookup(str) : str
86
+
87
+ if (!mime) {
88
+ return false
89
+ }
90
+
91
+ // TODO: use content-type or other module
92
+ if (mime.indexOf('charset') === -1) {
93
+ var charset = exports.charset(mime)
94
+ if (charset) mime += '; charset=' + charset.toLowerCase()
95
+ }
96
+
97
+ return mime
98
+ }
99
+
100
+ /**
101
+ * Get the default extension for a MIME type.
102
+ *
103
+ * @param {string} type
104
+ * @return {false|string}
105
+ */
106
+
107
+ function extension (type) {
108
+ if (!type || typeof type !== 'string') {
109
+ return false
110
+ }
111
+
112
+ // TODO: use media-typer
113
+ var match = EXTRACT_TYPE_REGEXP.exec(type)
114
+
115
+ // get extensions
116
+ var exts = match && exports.extensions[match[1].toLowerCase()]
117
+
118
+ if (!exts || !exts.length) {
119
+ return false
120
+ }
121
+
122
+ return exts[0]
123
+ }
124
+
125
+ /**
126
+ * Lookup the MIME type for a file path/extension.
127
+ *
128
+ * @param {string} path
129
+ * @return {false|string}
130
+ */
131
+
132
+ function lookup (path) {
133
+ if (!path || typeof path !== 'string') {
134
+ return false
135
+ }
136
+
137
+ // get the extension ("ext" or ".ext" or full path)
138
+ var extension = extname('x.' + path)
139
+ .toLowerCase()
140
+ .slice(1)
141
+
142
+ if (!extension) {
143
+ return false
144
+ }
145
+
146
+ return exports.types[extension] || false
147
+ }
148
+
149
+ /**
150
+ * Populate the extensions and types maps.
151
+ * @private
152
+ */
153
+
154
+ function populateMaps (extensions, types) {
155
+ Object.keys(db).forEach(function forEachMimeType (type) {
156
+ var mime = db[type]
157
+ var exts = mime.extensions
158
+
159
+ if (!exts || !exts.length) {
160
+ return
161
+ }
162
+
163
+ // mime -> extensions
164
+ extensions[type] = exts
165
+
166
+ // extension -> mime
167
+ for (var i = 0; i < exts.length; i++) {
168
+ var extension = exts[i]
169
+ types[extension] = _preferredType(extension, types[extension], type)
170
+
171
+ // DELETE (eventually): Capture extension->type maps that change as a
172
+ // result of switching to mime-score. This is just to help make reviewing
173
+ // PR #119 easier, and can be removed once that PR is approved.
174
+ const legacyType = _preferredTypeLegacy(
175
+ extension,
176
+ types[extension],
177
+ type
178
+ )
179
+ if (legacyType !== types[extension]) {
180
+ exports._extensionConflicts.push([extension, legacyType, types[extension]])
181
+ }
182
+ }
183
+ })
184
+ }
185
+
186
+ // Resolve type conflict using mime-score
187
+ function _preferredType (ext, type0, type1) {
188
+ var score0 = type0 ? mimeScore(type0, db[type0].source) : 0
189
+ var score1 = type1 ? mimeScore(type1, db[type1].source) : 0
190
+
191
+ return score0 > score1 ? type0 : type1
192
+ }
193
+
194
+ // Resolve type conflict using pre-mime-score logic
195
+ function _preferredTypeLegacy (ext, type0, type1) {
196
+ var SOURCE_RANK = ['nginx', 'apache', undefined, 'iana']
197
+
198
+ var score0 = type0 ? SOURCE_RANK.indexOf(db[type0].source) : 0
199
+ var score1 = type1 ? SOURCE_RANK.indexOf(db[type1].source) : 0
200
+
201
+ if (
202
+ exports.types[extension] !== 'application/octet-stream' &&
203
+ (score0 > score1 ||
204
+ (score0 === score1 &&
205
+ exports.types[extension]?.slice(0, 12) === 'application/'))
206
+ ) {
207
+ return type0
208
+ }
209
+
210
+ return score0 > score1 ? type0 : type1
211
+ }
@@ -0,0 +1,57 @@
1
+ // 'mime-score' back-ported to CommonJS
2
+
3
+ // Score RFC facets (see https://tools.ietf.org/html/rfc6838#section-3)
4
+ var FACET_SCORES = {
5
+ 'prs.': 100,
6
+ 'x-': 200,
7
+ 'x.': 300,
8
+ 'vnd.': 400,
9
+ default: 900
10
+ }
11
+
12
+ // Score mime source (Logic originally from `jshttp/mime-types` module)
13
+ var SOURCE_SCORES = {
14
+ nginx: 10,
15
+ apache: 20,
16
+ iana: 40,
17
+ default: 30 // definitions added by `jshttp/mime-db` project?
18
+ }
19
+
20
+ var TYPE_SCORES = {
21
+ // prefer application/xml over text/xml
22
+ // prefer application/rtf over text/rtf
23
+ application: 1,
24
+
25
+ // prefer font/woff over application/font-woff
26
+ font: 2,
27
+
28
+ // prefer video/mp4 over audio/mp4 over application/mp4
29
+ // See https://www.rfc-editor.org/rfc/rfc4337.html#section-2
30
+ audio: 2,
31
+ video: 3,
32
+
33
+ default: 0
34
+ }
35
+
36
+ /**
37
+ * Get each component of the score for a mime type. The sum of these is the
38
+ * total score. The higher the score, the more "official" the type.
39
+ */
40
+ module.exports = function mimeScore (mimeType, source = 'default') {
41
+ if (mimeType === 'application/octet-stream') {
42
+ return 0
43
+ }
44
+
45
+ const [type, subtype] = mimeType.split('/')
46
+
47
+ const facet = subtype.replace(/(\.|x-).*/, '$1')
48
+
49
+ const facetScore = FACET_SCORES[facet] || FACET_SCORES.default
50
+ const sourceScore = SOURCE_SCORES[source] || SOURCE_SCORES.default
51
+ const typeScore = TYPE_SCORES[type] || TYPE_SCORES.default
52
+
53
+ // All else being equal prefer shorter types
54
+ const lengthScore = 1 - mimeType.length / 100
55
+
56
+ return facetScore + sourceScore + typeScore + lengthScore
57
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "mime-types",
3
+ "description": "The ultimate javascript content-type utility.",
4
+ "version": "3.0.2",
5
+ "contributors": [
6
+ "Douglas Christopher Wilson <doug@somethingdoug.com>",
7
+ "Jeremiah Senkpiel <fishrock123@rocketmail.com> (https://searchbeam.jit.su)",
8
+ "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
9
+ ],
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "mime",
13
+ "types"
14
+ ],
15
+ "repository": "jshttp/mime-types",
16
+ "funding": {
17
+ "type": "opencollective",
18
+ "url": "https://opencollective.com/express"
19
+ },
20
+ "dependencies": {
21
+ "mime-db": "^1.54.0"
22
+ },
23
+ "devDependencies": {
24
+ "eslint": "8.33.0",
25
+ "eslint-config-standard": "14.1.1",
26
+ "eslint-plugin-import": "2.32.0",
27
+ "eslint-plugin-markdown": "3.0.1",
28
+ "eslint-plugin-node": "11.1.0",
29
+ "eslint-plugin-promise": "6.6.0",
30
+ "eslint-plugin-standard": "4.1.0",
31
+ "mocha": "10.8.2",
32
+ "nyc": "15.1.0"
33
+ },
34
+ "files": [
35
+ "HISTORY.md",
36
+ "LICENSE",
37
+ "index.js",
38
+ "mimeScore.js"
39
+ ],
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "scripts": {
44
+ "lint": "eslint .",
45
+ "test": "mocha --reporter spec test/test.js",
46
+ "test-ci": "nyc --reporter=lcov --reporter=text npm test",
47
+ "test-cov": "nyc --reporter=html --reporter=text npm test"
48
+ }
49
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "orient-cli",
3
- "version": "0.2.1",
4
- "description": "ORIENT CLI — a spec-driven coding agent with dual modes. Plan/Execute for fast feature work, ORIENT framework for beast-mode spec-driven development. Forked from pi-mono.",
3
+ "version": "0.3.0",
4
+ "description": "ORIENT CLI — a spec-driven coding agent with dual modes, compass banner, tier-aware research, and built-in browser automation. Plan/Execute for fast feature work, ORIENT framework for beast-mode spec-driven development. Ships with @playwright/cli for browser tools and notebooklm-py for tier-3 Research.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -14,6 +14,7 @@
14
14
  "skills",
15
15
  "prompts",
16
16
  "docs",
17
+ "scripts/postinstall.mjs",
17
18
  "README.md",
18
19
  "NOTICE.md",
19
20
  "LICENSE"
@@ -22,12 +23,15 @@
22
23
  "dev": "tsc --watch --preserveWatchOutput",
23
24
  "build": "tsc",
24
25
  "clean": "rm -rf dist",
26
+ "postinstall": "node scripts/postinstall.mjs",
25
27
  "prepublishOnly": "npm run clean && npm run build"
26
28
  },
27
29
  "dependencies": {
28
- "@orient-cli/coding-agent": "^0.2.0",
29
- "@orient-cli/agent-core": "^0.2.0",
30
- "@orient-cli/ai": "^0.2.0",
30
+ "@orient-cli/coding-agent": "^0.3.0",
31
+ "@orient-cli/agent-core": "^0.3.0",
32
+ "@orient-cli/ai": "^0.3.0",
33
+ "@playwright/cli": "^0.1.7",
34
+ "playwright": "^1.60.0-alpha",
31
35
  "@sinclair/typebox": "^0.34.0",
32
36
  "@anthropic-ai/sdk": "^0.73.0",
33
37
  "@aws-sdk/client-bedrock-runtime": "^3.983.0",
@@ -99,6 +103,9 @@
99
103
  "llm",
100
104
  "plan-execute",
101
105
  "workflow",
102
- "notebooklm"
106
+ "notebooklm",
107
+ "playwright",
108
+ "browser-automation",
109
+ "research"
103
110
  ]
104
111
  }
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * orient-cli postinstall script
4
+ * ==============================
5
+ *
6
+ * Runs once after `npm install -g orient-cli` (or a local install that
7
+ * includes orient-cli as a dep). Fetches the two heavyweight integrations
8
+ * the CLI ships by default so users get a fully working install in one
9
+ * command:
10
+ *
11
+ * 1. Chromium browser binary via `npx playwright install chromium`
12
+ * (~170 MB). Required by @playwright/cli, which ships as a regular
13
+ * dependency. Without this, every browser tool fails with
14
+ * "Executable doesn't exist".
15
+ *
16
+ * 2. `notebooklm-py[browser]` Python package via pip. Required by
17
+ * tier-3 Research phase. The Python-side `playwright install
18
+ * chromium` is ALSO run here so the notebooklm login flow (which
19
+ * opens a headed browser) has the browser binary it needs when
20
+ * the user eventually triggers it.
21
+ *
22
+ * The script is designed to degrade gracefully:
23
+ *
24
+ * - Any failure (no Python, no pip, network error, missing perms,
25
+ * etc.) is logged to stdout but does NOT block the orient-cli
26
+ * install. Users can always re-run the bits they need via
27
+ * `/orient.playwright.setup` and `/orient.research.setup` from
28
+ * inside the CLI.
29
+ *
30
+ * - `ORIENT_SKIP_POSTINSTALL=1` completely opts out — useful for CI
31
+ * containers that do their own browser provisioning, and for air-
32
+ * gapped installs. The two manual setup commands still work.
33
+ *
34
+ * - `ORIENT_SKIP_PLAYWRIGHT=1` and `ORIENT_SKIP_NOTEBOOKLM=1` let
35
+ * the user skip one integration without the other.
36
+ *
37
+ * - npm's `--ignore-scripts` flag disables the whole thing the way
38
+ * npm does for any postinstall hook.
39
+ *
40
+ * This script is intentionally pure Node with no extra dependencies so
41
+ * it runs before orient-cli's own node_modules have been constructed.
42
+ */
43
+
44
+ import { spawn, spawnSync } from "node:child_process";
45
+ import { existsSync } from "node:fs";
46
+
47
+ const log = (msg) => process.stdout.write(`[orient-cli postinstall] ${msg}\n`);
48
+ const warn = (msg) => process.stdout.write(`[orient-cli postinstall] ⚠ ${msg}\n`);
49
+
50
+ // ============================================================================
51
+ // Opt-outs
52
+ // ============================================================================
53
+
54
+ if (process.env.ORIENT_SKIP_POSTINSTALL === "1") {
55
+ log("ORIENT_SKIP_POSTINSTALL=1 — skipping all postinstall steps.");
56
+ log("Run /orient.playwright.setup and /orient.research.setup from inside orient later if you need them.");
57
+ process.exit(0);
58
+ }
59
+
60
+ // Skip inside CI if the caller hasn't explicitly opted in. CI
61
+ // environments rarely want a 170 MB chromium download unless they
62
+ // asked for it.
63
+ if (process.env.CI && !process.env.ORIENT_FORCE_POSTINSTALL) {
64
+ log("CI=1 detected — skipping postinstall (set ORIENT_FORCE_POSTINSTALL=1 to override).");
65
+ process.exit(0);
66
+ }
67
+
68
+ // ============================================================================
69
+ // Utilities
70
+ // ============================================================================
71
+
72
+ function which(binary) {
73
+ try {
74
+ const result = spawnSync(process.platform === "win32" ? "where" : "which", [binary], {
75
+ stdio: ["ignore", "pipe", "ignore"],
76
+ });
77
+ if (result.status === 0) {
78
+ const out = result.stdout.toString().trim();
79
+ return out.split(/\r?\n/)[0] || null;
80
+ }
81
+ } catch {
82
+ // Fall through.
83
+ }
84
+ return null;
85
+ }
86
+
87
+ function run(cmd, args, options = {}) {
88
+ return new Promise((resolve) => {
89
+ const proc = spawn(cmd, args, {
90
+ stdio: "inherit",
91
+ shell: process.platform === "win32",
92
+ ...options,
93
+ });
94
+ proc.on("error", (err) => {
95
+ warn(`${cmd} ${args.join(" ")} failed to spawn: ${err.message}`);
96
+ resolve(false);
97
+ });
98
+ proc.on("close", (code) => resolve(code === 0));
99
+ });
100
+ }
101
+
102
+ function detectPython() {
103
+ for (const candidate of ["python3", "python"]) {
104
+ if (which(candidate)) return candidate;
105
+ }
106
+ return null;
107
+ }
108
+
109
+ function pipArgsFor(python) {
110
+ // `python -m pip` is the only invocation that's guaranteed to
111
+ // match the Python we detected. A standalone `pip` on PATH can
112
+ // point at a different interpreter.
113
+ return [python, "-m", "pip"];
114
+ }
115
+
116
+ // ============================================================================
117
+ // Step 1 — Chromium via playwright (Node)
118
+ // ============================================================================
119
+
120
+ async function installChromium() {
121
+ if (process.env.ORIENT_SKIP_PLAYWRIGHT === "1") {
122
+ log("ORIENT_SKIP_PLAYWRIGHT=1 — skipping chromium install.");
123
+ return;
124
+ }
125
+
126
+ log("Installing Chromium browser binary (npx playwright install chromium, ~170 MB)…");
127
+ const ok = await run("npx", ["--yes", "playwright", "install", "chromium"]);
128
+ if (ok) {
129
+ log("✓ Chromium installed. `playwright-cli` browser tools are ready.");
130
+ } else {
131
+ warn(
132
+ "Chromium install failed. Run `npx playwright install chromium` manually, or `/orient.playwright.setup` from inside orient.",
133
+ );
134
+ }
135
+ }
136
+
137
+ // ============================================================================
138
+ // Step 2 — notebooklm-py[browser] via pip (Python)
139
+ // ============================================================================
140
+
141
+ async function installNotebookLM() {
142
+ if (process.env.ORIENT_SKIP_NOTEBOOKLM === "1") {
143
+ log("ORIENT_SKIP_NOTEBOOKLM=1 — skipping notebooklm-py install.");
144
+ return;
145
+ }
146
+
147
+ const python = detectPython();
148
+ if (!python) {
149
+ warn(
150
+ "Python 3 not found on PATH. notebooklm-py install skipped — install Python 3.10+ and run `/orient.research.setup` from inside orient later.",
151
+ );
152
+ return;
153
+ }
154
+
155
+ const pip = pipArgsFor(python);
156
+ const pipCheck = spawnSync(pip[0], [...pip.slice(1), "--version"], { stdio: "ignore" });
157
+ if (pipCheck.status !== 0) {
158
+ warn(
159
+ `pip not available for ${python}. Install pip (e.g. \`sudo apt install python3-pip\`) and run \`/orient.research.setup\` from inside orient later.`,
160
+ );
161
+ return;
162
+ }
163
+
164
+ log(`Installing notebooklm-py[browser] via ${pip.join(" ")} install --user …`);
165
+ const pipOk = await run(pip[0], [...pip.slice(1), "install", "--user", "notebooklm-py[browser]"]);
166
+ if (!pipOk) {
167
+ warn(
168
+ "notebooklm-py install failed. Run `pip install --user 'notebooklm-py[browser]'` manually, or `/orient.research.setup` from inside orient.",
169
+ );
170
+ return;
171
+ }
172
+ log("✓ notebooklm-py[browser] installed.");
173
+
174
+ // notebooklm-py needs its own chromium for the login flow. This is
175
+ // the same binary the Node-side playwright already fetched above,
176
+ // but the Python-side playwright maintains its own cache, so we
177
+ // install it again explicitly. The download is deduplicated in
178
+ // most environments because Playwright's binary store is shared
179
+ // across language bindings in its default location.
180
+ log("Installing Chromium for notebooklm login flow (python -m playwright install chromium)…");
181
+ const pwOk = await run(python, ["-m", "playwright", "install", "chromium"]);
182
+ if (pwOk) {
183
+ log("✓ Chromium installed for Python-side playwright.");
184
+ } else {
185
+ warn(
186
+ "Python-side Chromium install failed — `notebooklm login` may fail until you run `python3 -m playwright install chromium` manually.",
187
+ );
188
+ }
189
+
190
+ log(
191
+ "notebooklm-py is installed but NOT yet authenticated. First sign-in happens automatically the first time you enter tier-3 Research in orient (or run `/orient.research.setup` manually).",
192
+ );
193
+ }
194
+
195
+ // ============================================================================
196
+ // Main
197
+ // ============================================================================
198
+
199
+ async function main() {
200
+ log("Finalising orient-cli install — fetching browser + Python integrations…");
201
+ try {
202
+ await installChromium();
203
+ } catch (err) {
204
+ warn(`Chromium install errored: ${err?.message ?? err}`);
205
+ }
206
+ try {
207
+ await installNotebookLM();
208
+ } catch (err) {
209
+ warn(`notebooklm-py install errored: ${err?.message ?? err}`);
210
+ }
211
+
212
+ // Web search is reimplemented directly in orient-cli TypeScript
213
+ // (see packages/orient/src/integrations/web-search/) against the
214
+ // same playwright binary we just installed. No additional install
215
+ // step — the chromium browser fetched above is all it needs. We
216
+ // log the capability so the user knows it's live.
217
+ log("✓ Built-in web search ready (bing → brave → duckduckgo, no API keys, derived from mrkrsl/web-search-mcp pattern).");
218
+
219
+ log("");
220
+ log("orient-cli is ready. What now:");
221
+ log(" $ orient # start the dual-mode CLI");
222
+ log(" shift+tab # toggle Plan ↔ Execute in-session");
223
+ log(" /orient.framework # enter beast-mode spec-driven workflow");
224
+ log(" /orient.research.setup # re-run the NotebookLM auth later");
225
+ log(" /orient.playwright.setup # re-fetch chromium if needed");
226
+ log("");
227
+ }
228
+
229
+ main().catch((err) => {
230
+ // Never block the orient-cli install on a postinstall failure —
231
+ // the CLI itself is usable without these extras, they just need
232
+ // to be re-run from inside orient.
233
+ warn(`postinstall error: ${err?.message ?? err}`);
234
+ process.exit(0);
235
+ });