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.
- package/dist/extensions/orient-playwright.d.ts +7 -0
- package/dist/extensions/orient-playwright.js +8 -0
- package/dist/extensions/orient-playwright.js.map +1 -0
- package/dist/extensions/orient-web-search.d.ts +5 -0
- package/dist/extensions/orient-web-search.js +6 -0
- package/dist/extensions/orient-web-search.js.map +1 -0
- package/dist/integrations/notebooklm/setup.d.ts +94 -0
- package/dist/integrations/notebooklm/setup.js +311 -0
- package/dist/integrations/notebooklm/setup.js.map +1 -0
- package/dist/integrations/playwright/bridge.d.ts +85 -0
- package/dist/integrations/playwright/bridge.js +225 -0
- package/dist/integrations/playwright/bridge.js.map +1 -0
- package/dist/integrations/playwright/playwright-extension.d.ts +22 -0
- package/dist/integrations/playwright/playwright-extension.js +209 -0
- package/dist/integrations/playwright/playwright-extension.js.map +1 -0
- package/dist/integrations/web-search/bridge.d.ts +79 -0
- package/dist/integrations/web-search/bridge.js +298 -0
- package/dist/integrations/web-search/bridge.js.map +1 -0
- package/dist/integrations/web-search/web-search-extension.d.ts +26 -0
- package/dist/integrations/web-search/web-search-extension.js +122 -0
- package/dist/integrations/web-search/web-search-extension.js.map +1 -0
- package/dist/orient/orient-extension.js +149 -1
- package/dist/orient/orient-extension.js.map +1 -1
- package/dist/package-paths.js +6 -1
- package/dist/package-paths.js.map +1 -1
- package/node_modules/@orient-cli/agent-core/package.json +2 -2
- package/node_modules/@orient-cli/ai/package.json +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.js +2 -2
- package/node_modules/@orient-cli/coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/node_modules/@orient-cli/coding-agent/package.json +4 -4
- package/node_modules/@orient-cli/tui/package.json +1 -1
- package/node_modules/mime-db/HISTORY.md +541 -0
- package/node_modules/mime-db/LICENSE +23 -0
- package/node_modules/mime-db/README.md +109 -0
- package/node_modules/mime-db/db.json +9342 -0
- package/node_modules/mime-db/index.js +12 -0
- package/node_modules/mime-db/package.json +56 -0
- package/node_modules/mime-types/HISTORY.md +428 -0
- package/node_modules/mime-types/LICENSE +23 -0
- package/node_modules/mime-types/README.md +126 -0
- package/node_modules/mime-types/index.js +211 -0
- package/node_modules/mime-types/mimeScore.js +57 -0
- package/node_modules/mime-types/package.json +49 -0
- package/package.json +13 -6
- 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.
|
|
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.
|
|
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.
|
|
29
|
-
"@orient-cli/agent-core": "^0.
|
|
30
|
-
"@orient-cli/ai": "^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
|
+
});
|