@corenel/cli 0.7.0 → 0.9.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/README.md +77 -68
- package/dist/main.js +466 -125
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,68 +1,77 @@
|
|
|
1
|
-
# @corenel/cli
|
|
2
|
-
|
|
3
|
-
The Corenel CLI — runs the host-agnostic agent harness in Node, in-process (no
|
|
4
|
-
transport). Early/beta: the headless proof the kernel runs outside the browser.
|
|
5
|
-
|
|
6
|
-
```bash
|
|
7
|
-
npx @corenel/cli
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
Requires Node 22+. Bring your provider key via the environment.
|
|
11
|
-
|
|
12
|
-
Running `corenel` with no command opens an interactive agent in the current
|
|
13
|
-
directory. On a terminal it opens the full terminal face; when output is piped,
|
|
14
|
-
under `TERM=dumb`, in CI, or with `--no-tui`, it falls back to a plain
|
|
15
|
-
line-by-line loop. `--tui` demands the full face and fails rather than falling
|
|
16
|
-
back. `--resume` reopens the last conversation.
|
|
17
|
-
|
|
18
|
-
Assistant replies render as markdown. More verbs (`run` / `ask` / `chat` /
|
|
19
|
-
`login`, and `start --sidecar`) are in progress.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
1
|
+
# @corenel/cli
|
|
2
|
+
|
|
3
|
+
The Corenel CLI — runs the host-agnostic agent harness in Node, in-process (no
|
|
4
|
+
transport). Early/beta: the headless proof the kernel runs outside the browser.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npx @corenel/cli
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Requires Node 22+. Bring your provider key via the environment.
|
|
11
|
+
|
|
12
|
+
Running `corenel` with no command opens an interactive agent in the current
|
|
13
|
+
directory. On a terminal it opens the full terminal face; when output is piped,
|
|
14
|
+
under `TERM=dumb`, in CI, or with `--no-tui`, it falls back to a plain
|
|
15
|
+
line-by-line loop. `--tui` demands the full face and fails rather than falling
|
|
16
|
+
back. `--resume` reopens the last conversation.
|
|
17
|
+
|
|
18
|
+
Assistant replies render as markdown. More verbs (`run` / `ask` / `chat` /
|
|
19
|
+
`login`, and `start --sidecar`) are in progress.
|
|
20
|
+
|
|
21
|
+
The API base is `--base`, then `CORENEL_API_BASE`, then the base recorded at
|
|
22
|
+
`corenel login`, then `https://api.corenel.ai/api`. Run from source
|
|
23
|
+
(`tsx src/cli.ts`), that last default becomes the local API
|
|
24
|
+
(`http://localhost:3010/api`) instead; `corenel auth` and `corenel doctor`
|
|
25
|
+
say so, and a `login` from source records the local base on its credential.
|
|
26
|
+
`--base` and `CORENEL_API_BASE` turn that off, and a recorded login base wins over
|
|
27
|
+
it; to move a credential between production and the local API, run
|
|
28
|
+
`corenel logout` before `corenel login`.
|
|
29
|
+
|
|
30
|
+
## Releasing
|
|
31
|
+
|
|
32
|
+
`@corenel/term` (the terminal face) is a real runtime dependency, not bundled
|
|
33
|
+
into `dist/main.js` -- see the note at the top of `build.mjs`. (`dist/cli.js`,
|
|
34
|
+
the bin, is only a Node >=22 check that then imports `main.js`.) `@corenel/term`'s
|
|
35
|
+
own dist is bundled but keeps `@corenel/harness` and `@corenel/protocol`
|
|
36
|
+
EXTERNAL (see `packages/term/build.mjs`) -- both the CLI and term import the
|
|
37
|
+
harness directly, and they must resolve to the SAME installed copy, or the
|
|
38
|
+
`llm-execution` feature guard and `--debug` logging silently apply to the
|
|
39
|
+
wrong module instance. That makes all four packages real runtime dependencies
|
|
40
|
+
of each other, and publish order matters:
|
|
41
|
+
|
|
42
|
+
pnpm --filter @corenel/protocol build && pnpm --filter @corenel/protocol publish
|
|
43
|
+
pnpm --filter @corenel/harness build && pnpm --filter @corenel/harness publish
|
|
44
|
+
pnpm --filter @corenel/term build && pnpm --filter @corenel/term publish
|
|
45
|
+
pnpm --filter @corenel/cli build && pnpm --filter @corenel/cli publish
|
|
46
|
+
|
|
47
|
+
Publishing any package against an unpublished dependency yields one that
|
|
48
|
+
cannot install at all, so `npx @corenel/cli` fails before any verb runs.
|
|
49
|
+
|
|
50
|
+
Publish with **pnpm, never npm**: this CLI depends on `@corenel/term` as
|
|
51
|
+
`workspace:*`, and pnpm rewrites the `workspace:` protocol to a real version at
|
|
52
|
+
pack/publish time while npm ships it verbatim -- an `npm publish` from this
|
|
53
|
+
workspace produces a manifest whose install fails with
|
|
54
|
+
`EUNSUPPORTEDPROTOCOL / Unsupported URL Type "workspace:"`, which is the very
|
|
55
|
+
breakage this section exists to prevent.
|
|
56
|
+
|
|
57
|
+
**All four versions must be bumped and published together, in the order
|
|
58
|
+
above, every release -- even one that only touches a single package.** pnpm
|
|
59
|
+
rewrites every `workspace:*` dependency to the EXACT current version of that
|
|
60
|
+
dependency at pack time. All four packages are pinned to the same harness
|
|
61
|
+
version today, so a normal `npm install` of `@corenel/cli` resolves to one
|
|
62
|
+
harness copy. Bump `@corenel/harness` and publish a new `@corenel/term`
|
|
63
|
+
without also republishing `@corenel/cli`, and the tree that installs
|
|
64
|
+
`@corenel/cli` gets the OLD harness version nested under it (frozen into the
|
|
65
|
+
manifest `@corenel/cli` published against) plus the NEW harness version
|
|
66
|
+
hoisted at the top level for `@corenel/term` -- two separate harness module
|
|
67
|
+
instances again, silently. That is exactly the bug this section's ordering
|
|
68
|
+
exists to prevent (see `harnessSingleInstance.test.ts` in this package's
|
|
69
|
+
`test/`): `registerGuard()`/`setAgentLogging()` calls made through one
|
|
70
|
+
instance configure a module the other never reads, so the `llm-execution`
|
|
71
|
+
feature gate silently stops running and `--debug` logging can't be turned
|
|
72
|
+
off. Every test in this repo stays green when that happens, because the
|
|
73
|
+
in-repo workspace is symlinked (one instance by construction) and the
|
|
74
|
+
build-output regression test only exercises that same symlinked layout --
|
|
75
|
+
neither one can see a real, version-skewed `npm install`.
|
|
76
|
+
|
|
77
|
+
Licensed under Elastic-2.0.
|
package/dist/main.js
CHANGED
|
@@ -390,6 +390,19 @@ var init_fileService = __esm({
|
|
|
390
390
|
await this.assertReal(a);
|
|
391
391
|
await writeFileAtomic(a, bytes, { shownPath: path });
|
|
392
392
|
}
|
|
393
|
+
/** O_APPEND, NOT atomic-rename: the point is that another process appending
|
|
394
|
+
* to the same file at the same moment lands too, and a rename would replace
|
|
395
|
+
* whatever it wrote. Same confinement as write. */
|
|
396
|
+
async append(path, content) {
|
|
397
|
+
const a = await this.writeTarget(path);
|
|
398
|
+
await fs2.mkdir(dirname2(a), { recursive: true });
|
|
399
|
+
await this.assertReal(a);
|
|
400
|
+
try {
|
|
401
|
+
await fs2.appendFile(a, content, "utf8");
|
|
402
|
+
} catch (e) {
|
|
403
|
+
throw diskFullError(path, e) ?? e;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
393
406
|
async create(path, content = "") {
|
|
394
407
|
const a = await this.writeTarget(path);
|
|
395
408
|
await fs2.mkdir(dirname2(a), { recursive: true });
|
|
@@ -7878,7 +7891,7 @@ var require_public_api = __commonJS({
|
|
|
7878
7891
|
}
|
|
7879
7892
|
return doc;
|
|
7880
7893
|
}
|
|
7881
|
-
function
|
|
7894
|
+
function parse3(src, reviver, options) {
|
|
7882
7895
|
let _reviver = void 0;
|
|
7883
7896
|
if (typeof reviver === "function") {
|
|
7884
7897
|
_reviver = reviver;
|
|
@@ -7919,7 +7932,7 @@ var require_public_api = __commonJS({
|
|
|
7919
7932
|
return value.toString(options);
|
|
7920
7933
|
return new Document.Document(value, _replacer, options).toString(options);
|
|
7921
7934
|
}
|
|
7922
|
-
exports.parse =
|
|
7935
|
+
exports.parse = parse3;
|
|
7923
7936
|
exports.parseAllDocuments = parseAllDocuments;
|
|
7924
7937
|
exports.parseDocument = parseDocument;
|
|
7925
7938
|
exports.stringify = stringify2;
|
|
@@ -8445,7 +8458,7 @@ var require_parse = __commonJS({
|
|
|
8445
8458
|
"../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/parse.js"(exports, module) {
|
|
8446
8459
|
"use strict";
|
|
8447
8460
|
var SemVer = require_semver();
|
|
8448
|
-
var
|
|
8461
|
+
var parse3 = (version2, options, throwErrors = false) => {
|
|
8449
8462
|
if (version2 instanceof SemVer) {
|
|
8450
8463
|
return version2;
|
|
8451
8464
|
}
|
|
@@ -8458,7 +8471,7 @@ var require_parse = __commonJS({
|
|
|
8458
8471
|
throw er;
|
|
8459
8472
|
}
|
|
8460
8473
|
};
|
|
8461
|
-
module.exports =
|
|
8474
|
+
module.exports = parse3;
|
|
8462
8475
|
}
|
|
8463
8476
|
});
|
|
8464
8477
|
|
|
@@ -8466,9 +8479,9 @@ var require_parse = __commonJS({
|
|
|
8466
8479
|
var require_valid = __commonJS({
|
|
8467
8480
|
"../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/valid.js"(exports, module) {
|
|
8468
8481
|
"use strict";
|
|
8469
|
-
var
|
|
8482
|
+
var parse3 = require_parse();
|
|
8470
8483
|
var valid = (version2, options) => {
|
|
8471
|
-
const v =
|
|
8484
|
+
const v = parse3(version2, options);
|
|
8472
8485
|
return v ? v.version : null;
|
|
8473
8486
|
};
|
|
8474
8487
|
module.exports = valid;
|
|
@@ -8479,9 +8492,9 @@ var require_valid = __commonJS({
|
|
|
8479
8492
|
var require_clean = __commonJS({
|
|
8480
8493
|
"../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/clean.js"(exports, module) {
|
|
8481
8494
|
"use strict";
|
|
8482
|
-
var
|
|
8495
|
+
var parse3 = require_parse();
|
|
8483
8496
|
var clean = (version2, options) => {
|
|
8484
|
-
const s =
|
|
8497
|
+
const s = parse3(version2.trim().replace(/^[=v]+/, ""), options);
|
|
8485
8498
|
return s ? s.version : null;
|
|
8486
8499
|
};
|
|
8487
8500
|
module.exports = clean;
|
|
@@ -8516,10 +8529,10 @@ var require_inc = __commonJS({
|
|
|
8516
8529
|
var require_diff = __commonJS({
|
|
8517
8530
|
"../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/diff.js"(exports, module) {
|
|
8518
8531
|
"use strict";
|
|
8519
|
-
var
|
|
8532
|
+
var parse3 = require_parse();
|
|
8520
8533
|
var diff = (version1, version2) => {
|
|
8521
|
-
const v1 =
|
|
8522
|
-
const v2 =
|
|
8534
|
+
const v1 = parse3(version1, null, true);
|
|
8535
|
+
const v2 = parse3(version2, null, true);
|
|
8523
8536
|
const comparison = v1.compare(v2);
|
|
8524
8537
|
if (comparison === 0) {
|
|
8525
8538
|
return null;
|
|
@@ -8590,9 +8603,9 @@ var require_patch = __commonJS({
|
|
|
8590
8603
|
var require_prerelease = __commonJS({
|
|
8591
8604
|
"../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/prerelease.js"(exports, module) {
|
|
8592
8605
|
"use strict";
|
|
8593
|
-
var
|
|
8606
|
+
var parse3 = require_parse();
|
|
8594
8607
|
var prerelease = (version2, options) => {
|
|
8595
|
-
const parsed =
|
|
8608
|
+
const parsed = parse3(version2, options);
|
|
8596
8609
|
return parsed && parsed.prerelease.length ? parsed.prerelease : null;
|
|
8597
8610
|
};
|
|
8598
8611
|
module.exports = prerelease;
|
|
@@ -8778,7 +8791,7 @@ var require_coerce = __commonJS({
|
|
|
8778
8791
|
"../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/coerce.js"(exports, module) {
|
|
8779
8792
|
"use strict";
|
|
8780
8793
|
var SemVer = require_semver();
|
|
8781
|
-
var
|
|
8794
|
+
var parse3 = require_parse();
|
|
8782
8795
|
var { safeRe: re, t } = require_re();
|
|
8783
8796
|
var coerce = (version2, options) => {
|
|
8784
8797
|
if (version2 instanceof SemVer) {
|
|
@@ -8813,7 +8826,7 @@ var require_coerce = __commonJS({
|
|
|
8813
8826
|
const patch = match[4] || "0";
|
|
8814
8827
|
const prerelease = options.includePrerelease && match[5] ? `-${match[5]}` : "";
|
|
8815
8828
|
const build = options.includePrerelease && match[6] ? `+${match[6]}` : "";
|
|
8816
|
-
return
|
|
8829
|
+
return parse3(`${major}.${minor}.${patch}${prerelease}${build}`, options);
|
|
8817
8830
|
};
|
|
8818
8831
|
module.exports = coerce;
|
|
8819
8832
|
}
|
|
@@ -8823,7 +8836,7 @@ var require_coerce = __commonJS({
|
|
|
8823
8836
|
var require_truncate = __commonJS({
|
|
8824
8837
|
"../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/truncate.js"(exports, module) {
|
|
8825
8838
|
"use strict";
|
|
8826
|
-
var
|
|
8839
|
+
var parse3 = require_parse();
|
|
8827
8840
|
var constants2 = require_constants();
|
|
8828
8841
|
var SemVer = require_semver();
|
|
8829
8842
|
var truncate2 = (version2, truncation, options) => {
|
|
@@ -8835,7 +8848,7 @@ var require_truncate = __commonJS({
|
|
|
8835
8848
|
};
|
|
8836
8849
|
var cloneInputVersion = (version2, options) => {
|
|
8837
8850
|
const versionStringToParse = version2 instanceof SemVer ? version2.version : version2;
|
|
8838
|
-
return
|
|
8851
|
+
return parse3(versionStringToParse, options);
|
|
8839
8852
|
};
|
|
8840
8853
|
var doTruncation = (version2, truncation) => {
|
|
8841
8854
|
if (isPrerelease(truncation)) {
|
|
@@ -9877,7 +9890,7 @@ var require_semver2 = __commonJS({
|
|
|
9877
9890
|
var constants2 = require_constants();
|
|
9878
9891
|
var SemVer = require_semver();
|
|
9879
9892
|
var identifiers = require_identifiers();
|
|
9880
|
-
var
|
|
9893
|
+
var parse3 = require_parse();
|
|
9881
9894
|
var valid = require_valid();
|
|
9882
9895
|
var clean = require_clean();
|
|
9883
9896
|
var inc = require_inc();
|
|
@@ -9916,7 +9929,7 @@ var require_semver2 = __commonJS({
|
|
|
9916
9929
|
var simplifyRange = require_simplify();
|
|
9917
9930
|
var subset = require_subset();
|
|
9918
9931
|
module.exports = {
|
|
9919
|
-
parse:
|
|
9932
|
+
parse: parse3,
|
|
9920
9933
|
valid,
|
|
9921
9934
|
clean,
|
|
9922
9935
|
inc,
|
|
@@ -12303,7 +12316,7 @@ var require_parser2 = __commonJS({
|
|
|
12303
12316
|
}
|
|
12304
12317
|
return buf;
|
|
12305
12318
|
};
|
|
12306
|
-
_proto.parse = function
|
|
12319
|
+
_proto.parse = function parse3() {
|
|
12307
12320
|
return new nodes.NodeList(0, 0, this.parseNodes());
|
|
12308
12321
|
};
|
|
12309
12322
|
_proto.parseAsRoot = function parseAsRoot() {
|
|
@@ -12312,7 +12325,7 @@ var require_parser2 = __commonJS({
|
|
|
12312
12325
|
return Parser2;
|
|
12313
12326
|
}(Obj);
|
|
12314
12327
|
module.exports = {
|
|
12315
|
-
parse: function
|
|
12328
|
+
parse: function parse3(src, extensions, opts) {
|
|
12316
12329
|
var p = new Parser(lexer.lex(src, opts));
|
|
12317
12330
|
if (extensions !== void 0) {
|
|
12318
12331
|
p.extensions = extensions;
|
|
@@ -13894,7 +13907,7 @@ var require_filters = __commonJS({
|
|
|
13894
13907
|
return r.copySafeness(str, res);
|
|
13895
13908
|
}
|
|
13896
13909
|
_exports.indent = indent;
|
|
13897
|
-
function
|
|
13910
|
+
function join14(arr, del, attr) {
|
|
13898
13911
|
del = del || "";
|
|
13899
13912
|
if (attr) {
|
|
13900
13913
|
arr = lib.map(arr, function(v) {
|
|
@@ -13903,7 +13916,7 @@ var require_filters = __commonJS({
|
|
|
13903
13916
|
}
|
|
13904
13917
|
return arr.join(del);
|
|
13905
13918
|
}
|
|
13906
|
-
_exports.join =
|
|
13919
|
+
_exports.join = join14;
|
|
13907
13920
|
function last(arr) {
|
|
13908
13921
|
return arr[arr.length - 1];
|
|
13909
13922
|
}
|
|
@@ -15246,8 +15259,8 @@ var require_precompile = __commonJS({
|
|
|
15246
15259
|
fs6.readdirSync(dir).forEach(function(file) {
|
|
15247
15260
|
var filepath = path.join(dir, file);
|
|
15248
15261
|
var subpath = filepath.substr(path.join(input, "/").length);
|
|
15249
|
-
var
|
|
15250
|
-
if (
|
|
15262
|
+
var stat2 = fs6.statSync(filepath);
|
|
15263
|
+
if (stat2 && stat2.isDirectory()) {
|
|
15251
15264
|
subpath += "/";
|
|
15252
15265
|
if (!match(subpath, opts.exclude)) {
|
|
15253
15266
|
addTemplates(filepath);
|
|
@@ -16426,8 +16439,8 @@ ${yamlContent}` : yamlContent;
|
|
|
16426
16439
|
if (resolvedInheritsRef.startsWith("./") || resolvedInheritsRef.startsWith("../") || resolvedInheritsRef.startsWith("/") || !resolvedInheritsRef.startsWith("@") && isPrompdFile(resolvedInheritsRef)) {
|
|
16427
16440
|
const sourceDir = context.fileSystem.dirname(context.sourceFile);
|
|
16428
16441
|
const parentPath = context.fileSystem.resolve(sourceDir, resolvedInheritsRef);
|
|
16429
|
-
const
|
|
16430
|
-
if (!
|
|
16442
|
+
const fileExists2 = await context.fileSystem.exists(parentPath);
|
|
16443
|
+
if (!fileExists2) {
|
|
16431
16444
|
const location = context.findLocation(/inherits:/);
|
|
16432
16445
|
context.addDiagnostic({
|
|
16433
16446
|
message: `Inherited file not found: "${resolvedInheritsRef}" (resolved to: ${parentPath})`,
|
|
@@ -16454,8 +16467,8 @@ ${yamlContent}` : yamlContent;
|
|
|
16454
16467
|
}
|
|
16455
16468
|
const packagePath = await this.resolvePackage(context, packageRef);
|
|
16456
16469
|
const fullFilePath = resolvePackageFile(packagePath, filePathInPackage);
|
|
16457
|
-
const
|
|
16458
|
-
if (!
|
|
16470
|
+
const fileExists2 = await context.fileSystem.exists(fullFilePath);
|
|
16471
|
+
if (!fileExists2) {
|
|
16459
16472
|
const location = context.findLocation(/inherits:/);
|
|
16460
16473
|
context.addDiagnostic({
|
|
16461
16474
|
message: `Inherited file not found in package: "${filePathInPackage}" (package: ${packageRef})`,
|
|
@@ -21103,7 +21116,7 @@ var require_extension = __commonJS({
|
|
|
21103
21116
|
if (dest[name] === void 0) dest[name] = [elem];
|
|
21104
21117
|
else dest[name].push(elem);
|
|
21105
21118
|
}
|
|
21106
|
-
function
|
|
21119
|
+
function parse3(header) {
|
|
21107
21120
|
const offers = /* @__PURE__ */ Object.create(null);
|
|
21108
21121
|
let params = /* @__PURE__ */ Object.create(null);
|
|
21109
21122
|
let mustUnescape = false;
|
|
@@ -21243,7 +21256,7 @@ var require_extension = __commonJS({
|
|
|
21243
21256
|
}).join(", ");
|
|
21244
21257
|
}).join(", ");
|
|
21245
21258
|
}
|
|
21246
|
-
module.exports = { format, parse:
|
|
21259
|
+
module.exports = { format, parse: parse3 };
|
|
21247
21260
|
}
|
|
21248
21261
|
});
|
|
21249
21262
|
|
|
@@ -21277,7 +21290,7 @@ var require_websocket = __commonJS({
|
|
|
21277
21290
|
var {
|
|
21278
21291
|
EventTarget: { addEventListener, removeEventListener }
|
|
21279
21292
|
} = require_event_target();
|
|
21280
|
-
var { format, parse:
|
|
21293
|
+
var { format, parse: parse3 } = require_extension();
|
|
21281
21294
|
var { toBuffer } = require_buffer_util();
|
|
21282
21295
|
var kAborted = Symbol("kAborted");
|
|
21283
21296
|
var protocolVersions = [8, 13];
|
|
@@ -21954,7 +21967,7 @@ var require_websocket = __commonJS({
|
|
|
21954
21967
|
}
|
|
21955
21968
|
let extensions;
|
|
21956
21969
|
try {
|
|
21957
|
-
extensions =
|
|
21970
|
+
extensions = parse3(secWebSocketExtensions);
|
|
21958
21971
|
} catch (err) {
|
|
21959
21972
|
const message = "Invalid Sec-WebSocket-Extensions header";
|
|
21960
21973
|
abortHandshake(websocket, socket, message);
|
|
@@ -22246,7 +22259,7 @@ var require_subprotocol = __commonJS({
|
|
|
22246
22259
|
"../../node_modules/.pnpm/ws@8.21.0/node_modules/ws/lib/subprotocol.js"(exports, module) {
|
|
22247
22260
|
"use strict";
|
|
22248
22261
|
var { tokenChars } = require_validation();
|
|
22249
|
-
function
|
|
22262
|
+
function parse3(header) {
|
|
22250
22263
|
const protocols = /* @__PURE__ */ new Set();
|
|
22251
22264
|
let start = -1;
|
|
22252
22265
|
let end = -1;
|
|
@@ -22282,7 +22295,7 @@ var require_subprotocol = __commonJS({
|
|
|
22282
22295
|
protocols.add(protocol);
|
|
22283
22296
|
return protocols;
|
|
22284
22297
|
}
|
|
22285
|
-
module.exports = { parse:
|
|
22298
|
+
module.exports = { parse: parse3 };
|
|
22286
22299
|
}
|
|
22287
22300
|
});
|
|
22288
22301
|
|
|
@@ -22691,7 +22704,7 @@ var require_websocket_server = __commonJS({
|
|
|
22691
22704
|
init_tools_node();
|
|
22692
22705
|
import { runAgent as runAgent2 } from "@corenel/harness/core/loop";
|
|
22693
22706
|
import { createChatClient as createChatClient2, setGatewayBase as setGatewayBase2 } from "@corenel/harness/providers/gateway";
|
|
22694
|
-
import { resolveContextWindow as
|
|
22707
|
+
import { resolveContextWindow as resolveContextWindow3, resolveContextWindowWithHydration } from "@corenel/harness/providers/contextWindow";
|
|
22695
22708
|
import { basename as basename5 } from "node:path";
|
|
22696
22709
|
import { randomUUID } from "node:crypto";
|
|
22697
22710
|
import { allTools as allTools3 } from "@corenel/harness/tools/builtins";
|
|
@@ -22708,9 +22721,9 @@ function configWorkspaceRoot(base) {
|
|
|
22708
22721
|
|
|
22709
22722
|
// src/cli.ts
|
|
22710
22723
|
import { FileRecallStore, UNATTACHED_SESSION } from "@corenel/harness/sessions/fileRecallStore";
|
|
22711
|
-
import {
|
|
22724
|
+
import { makeSessionFoldStorage } from "@corenel/harness/sessions/foldStorage";
|
|
22712
22725
|
import { makeSummarizer } from "@corenel/harness/core/summarize";
|
|
22713
|
-
import { stateDirName as stateDirName7, sessionsRoot } from "@corenel/protocol";
|
|
22726
|
+
import { stateDirName as stateDirName7, sessionsRoot, isSourceRun } from "@corenel/protocol";
|
|
22714
22727
|
import { PermissionService as PermissionService6 } from "@corenel/harness/guardrail/permission-service";
|
|
22715
22728
|
|
|
22716
22729
|
// src/nodeContext.ts
|
|
@@ -24865,7 +24878,7 @@ async function deviceLogin(opts) {
|
|
|
24865
24878
|
);
|
|
24866
24879
|
}
|
|
24867
24880
|
};
|
|
24868
|
-
const dc = await post("/oauth/device/code", { client_id: "corenel-cli", scope: "agent" });
|
|
24881
|
+
const dc = await post("/oauth/device/code", { client_id: "corenel-cli", scope: "agent pair" });
|
|
24869
24882
|
if (!dc.device_code) throw new Error("device authorization failed");
|
|
24870
24883
|
(opts.open ?? defaultOpen)(dc.verification_uri, dc.user_code);
|
|
24871
24884
|
let interval = opts.pollMs ?? Math.max(MIN_POLL_MS, (dc.interval ?? 5) * 1e3);
|
|
@@ -24905,6 +24918,7 @@ import { createChatClient, setGatewayBase } from "@corenel/harness/providers/gat
|
|
|
24905
24918
|
// src/apiBase.ts
|
|
24906
24919
|
init_tools_node();
|
|
24907
24920
|
import { DEFAULT_API_BASE as DEFAULT_API_BASE2 } from "@corenel/protocol";
|
|
24921
|
+
import { DEV_PROFILE } from "@corenel/protocol";
|
|
24908
24922
|
var InvalidApiBaseError = class extends Error {
|
|
24909
24923
|
constructor(value, why) {
|
|
24910
24924
|
super(`invalid API base ${JSON.stringify(value)} -- ${why}`);
|
|
@@ -24913,17 +24927,39 @@ var InvalidApiBaseError = class extends Error {
|
|
|
24913
24927
|
this.name = "InvalidApiBaseError";
|
|
24914
24928
|
}
|
|
24915
24929
|
};
|
|
24916
|
-
function isLoopback(
|
|
24917
|
-
const h =
|
|
24930
|
+
function isLoopback(hostname2) {
|
|
24931
|
+
const h = hostname2.replace(/^\[|\]$/g, "").toLowerCase();
|
|
24918
24932
|
return h === "localhost" || h === "127.0.0.1" || h === "::1" || h.endsWith(".localhost");
|
|
24919
24933
|
}
|
|
24920
24934
|
var warnedCleartext = false;
|
|
24935
|
+
var sourceRunDefault = false;
|
|
24936
|
+
function setSourceRun(sourceRun) {
|
|
24937
|
+
sourceRunDefault = sourceRun;
|
|
24938
|
+
}
|
|
24939
|
+
function applySourceRunDevBase(resolved, sourceRun) {
|
|
24940
|
+
if (!sourceRun || resolved.source !== "default") return resolved;
|
|
24941
|
+
return { base: DEV_PROFILE.apiBase, source: "dev-profile" };
|
|
24942
|
+
}
|
|
24943
|
+
function cliDevRelayBases(resolved, sourceRun = sourceRunDefault) {
|
|
24944
|
+
if (resolved.source === "dev-profile") return [resolved.base];
|
|
24945
|
+
if (sourceRun && resolved.source === "credential" && resolved.base.replace(/\/+$/, "") === DEV_PROFILE.apiBase) return [resolved.base];
|
|
24946
|
+
return [];
|
|
24947
|
+
}
|
|
24948
|
+
function apiBaseNote(source) {
|
|
24949
|
+
return source === "dev-profile" ? "dev profile: running from source" : void 0;
|
|
24950
|
+
}
|
|
24921
24951
|
async function apiBase(explicit, opts = {}) {
|
|
24922
|
-
|
|
24923
|
-
|
|
24924
|
-
|
|
24925
|
-
|
|
24926
|
-
|
|
24952
|
+
return (await resolveCliApiBase(explicit, opts)).base;
|
|
24953
|
+
}
|
|
24954
|
+
async function resolveCliApiBase(explicit, opts = {}) {
|
|
24955
|
+
const resolved = applySourceRunDevBase(
|
|
24956
|
+
await resolveApiBase({
|
|
24957
|
+
explicit,
|
|
24958
|
+
env: opts.env ?? process.env,
|
|
24959
|
+
...opts.stateDir ? { stateDir: opts.stateDir } : {}
|
|
24960
|
+
}),
|
|
24961
|
+
opts.sourceRun ?? sourceRunDefault
|
|
24962
|
+
);
|
|
24927
24963
|
const value = resolved.base;
|
|
24928
24964
|
let url;
|
|
24929
24965
|
try {
|
|
@@ -24941,7 +24977,7 @@ async function apiBase(explicit, opts = {}) {
|
|
|
24941
24977
|
`
|
|
24942
24978
|
);
|
|
24943
24979
|
}
|
|
24944
|
-
return
|
|
24980
|
+
return resolved;
|
|
24945
24981
|
}
|
|
24946
24982
|
|
|
24947
24983
|
// src/runAgent.ts
|
|
@@ -25257,7 +25293,9 @@ function helpText(version2) {
|
|
|
25257
25293
|
" --model <id> model for this turn (default gpt-4o-mini)",
|
|
25258
25294
|
" --base <url> API base (default https://api.corenel.ai/api, or CORENEL_API_BASE)",
|
|
25259
25295
|
" --policy <name|path> a builtin policy name (e.g. read-only) or a policy YAML file;",
|
|
25260
|
-
" default is
|
|
25296
|
+
" default is the per-project policy (policies/ws-<id>.yaml in your home",
|
|
25297
|
+
" state dir), else the user policy.yaml there, else standard. A",
|
|
25298
|
+
" policy.yaml inside the project folder is ignored.",
|
|
25261
25299
|
" --allow-mutations allow write/shell/other mutating tools (run/run-agent refuse them",
|
|
25262
25300
|
" outright without this; interactively it skips the per-command",
|
|
25263
25301
|
" prompt instead). --allow-shell still works as an alias.",
|
|
@@ -25342,23 +25380,27 @@ async function authState(resolve4 = (env2) => resolveNodeCredential({ env: env2
|
|
|
25342
25380
|
const source = r.source === "env" ? "env" : "file";
|
|
25343
25381
|
return { source, path: r.path ?? authTokenPath(), hint: hintOf(r.token) };
|
|
25344
25382
|
}
|
|
25345
|
-
function describeAuth(s, base) {
|
|
25383
|
+
function describeAuth(s, base, source) {
|
|
25384
|
+
const note = source ? apiBaseNote(source) : void 0;
|
|
25385
|
+
const apiLine = ` api ${base}${note ? ` (${note})` : ""}`;
|
|
25346
25386
|
if (s.source === "none") {
|
|
25347
25387
|
return [
|
|
25348
25388
|
"Not signed in.",
|
|
25349
25389
|
` Run \`corenel login\`, or set CORENEL_TOKEN=<token>.`,
|
|
25350
|
-
` Token would be stored at ${s.path}
|
|
25351
|
-
|
|
25390
|
+
` Token would be stored at ${s.path}`,
|
|
25391
|
+
note ? apiLine : null
|
|
25392
|
+
].filter(Boolean).join("\n");
|
|
25352
25393
|
}
|
|
25353
25394
|
return [
|
|
25354
25395
|
`Signed in (${s.source === "env" ? "CORENEL_TOKEN environment variable" : s.path}).`,
|
|
25355
25396
|
` token ${s.hint}`,
|
|
25356
|
-
|
|
25397
|
+
apiLine,
|
|
25357
25398
|
s.source === "env" ? " NOTE: the environment wins over any stored token, so `corenel logout` will not change this." : null
|
|
25358
25399
|
].filter(Boolean).join("\n");
|
|
25359
25400
|
}
|
|
25360
25401
|
async function runAuth(out = (s) => process.stdout.write(s)) {
|
|
25361
|
-
|
|
25402
|
+
const { base, source } = await resolveCliApiBase();
|
|
25403
|
+
out(`${describeAuth(await authState(), base, source)}
|
|
25362
25404
|
`);
|
|
25363
25405
|
}
|
|
25364
25406
|
async function runLogout(out = (s) => process.stdout.write(s)) {
|
|
@@ -25417,6 +25459,7 @@ var KNOWN_KEYS = [
|
|
|
25417
25459
|
"addUnder",
|
|
25418
25460
|
"cloneUnder",
|
|
25419
25461
|
"allowLan",
|
|
25462
|
+
"behindTlsProxy",
|
|
25420
25463
|
"allowOrigins",
|
|
25421
25464
|
"tlsCert",
|
|
25422
25465
|
"tlsKey",
|
|
@@ -25438,7 +25481,7 @@ var KNOWN_KEYS = [
|
|
|
25438
25481
|
"mcpServers",
|
|
25439
25482
|
"mcpAllow",
|
|
25440
25483
|
"identityIssuer",
|
|
25441
|
-
"
|
|
25484
|
+
"org",
|
|
25442
25485
|
"push",
|
|
25443
25486
|
"audit",
|
|
25444
25487
|
"sendTelemetry",
|
|
@@ -25707,17 +25750,19 @@ async function checkSidecarEnv(base, env, stateDir = corenelDir()) {
|
|
|
25707
25750
|
return { name: "sidecar", status: "fail", detail: `${base} (env: ${env}) -- ${e instanceof Error ? e.message : String(e)}` };
|
|
25708
25751
|
}
|
|
25709
25752
|
}
|
|
25753
|
+
function apiCheck(resolved) {
|
|
25754
|
+
const note = apiBaseNote(resolved.source);
|
|
25755
|
+
const detail = note ? `${resolved.base} (${note}; installed default is ${DEFAULT_API_BASE2})` : resolved.base === DEFAULT_API_BASE2 ? resolved.base : `${resolved.base} (overridden; default is ${DEFAULT_API_BASE2})`;
|
|
25756
|
+
return { name: "api", status: "ok", detail };
|
|
25757
|
+
}
|
|
25710
25758
|
async function collectChecks(f = fetch, modelId = DEFAULT_MODEL, env) {
|
|
25711
|
-
const
|
|
25759
|
+
const resolvedBase = await resolveCliApiBase();
|
|
25760
|
+
const base = resolvedBase.base;
|
|
25712
25761
|
const checks = [
|
|
25713
25762
|
{ name: "binary", status: "ok", detail: selfPath() },
|
|
25714
25763
|
{ name: "version", status: "ok", detail: `@corenel/cli ${version()}` },
|
|
25715
25764
|
{ name: "node", status: "ok", detail: process.version },
|
|
25716
|
-
|
|
25717
|
-
name: "api",
|
|
25718
|
-
status: "ok",
|
|
25719
|
-
detail: base === DEFAULT_API_BASE2 ? base : `${base} (overridden; default is ${DEFAULT_API_BASE2})`
|
|
25720
|
-
}
|
|
25765
|
+
apiCheck(resolvedBase)
|
|
25721
25766
|
];
|
|
25722
25767
|
try {
|
|
25723
25768
|
const u = new URL(base);
|
|
@@ -25765,7 +25810,7 @@ import { createInterface } from "node:readline";
|
|
|
25765
25810
|
import { PermissionService as PermissionService4 } from "@corenel/harness/guardrail/permission-service";
|
|
25766
25811
|
import { STANDARD_POLICY as STANDARD_POLICY2 } from "@corenel/harness/guardrail/builtins";
|
|
25767
25812
|
import { approvalsHooks } from "@corenel/harness/approvals";
|
|
25768
|
-
import { TermSession, parseSuggestionChoice, suggestionKeys, suggestionLine, suggestionOutcome } from "@corenel/term/session";
|
|
25813
|
+
import { TermSession, startupResumeOptions, parseSuggestionChoice, suggestionKeys, suggestionLine, suggestionOutcome } from "@corenel/term/session";
|
|
25769
25814
|
import { describeToolCall, formatOrd } from "@corenel/term/describe";
|
|
25770
25815
|
import { buildWritePreview } from "@corenel/term/writePreview";
|
|
25771
25816
|
import { formatEndingChange } from "@corenel/term/diff";
|
|
@@ -25923,10 +25968,10 @@ async function runRepl(deps) {
|
|
|
25923
25968
|
}
|
|
25924
25969
|
});
|
|
25925
25970
|
if (deps.resume) {
|
|
25926
|
-
await session.resume(deps.resume.id);
|
|
25971
|
+
await session.resume(deps.resume.id, startupResumeOptions(deps.resume));
|
|
25927
25972
|
const restored = session.getSnapshot();
|
|
25928
25973
|
if (restored.session) {
|
|
25929
|
-
writeLine(` resumed ${restored.session.id} - ${restored.committed.length} messages
|
|
25974
|
+
writeLine(` resumed ${restored.session.id} - ${restored.committed.filter((i) => i.kind !== "note").length} messages
|
|
25930
25975
|
`);
|
|
25931
25976
|
}
|
|
25932
25977
|
}
|
|
@@ -26213,6 +26258,44 @@ ${rules(r.workspace)}`);
|
|
|
26213
26258
|
import { approvalsHooks as approvalsHooks2 } from "@corenel/harness/approvals";
|
|
26214
26259
|
import { createSessionPersistence } from "@corenel/term/persist";
|
|
26215
26260
|
|
|
26261
|
+
// src/resumeModel.ts
|
|
26262
|
+
import { estimateFill, COMPACT_AT_FILL } from "@corenel/harness/core/compactHistory";
|
|
26263
|
+
import { resolveContextWindow as resolveContextWindow2 } from "@corenel/harness/providers/contextWindow";
|
|
26264
|
+
var DEFAULT_MODEL2 = "gpt-4o-mini";
|
|
26265
|
+
function resumeModel(flagModel, stored, fallback) {
|
|
26266
|
+
return flagModel || stored || fallback;
|
|
26267
|
+
}
|
|
26268
|
+
async function storedModelOf(resume, persistence) {
|
|
26269
|
+
if (!resume) return void 0;
|
|
26270
|
+
const loaded = await persistence.load(resume.id);
|
|
26271
|
+
if (!loaded) return void 0;
|
|
26272
|
+
return { id: loaded.id, messages: loaded.messages, ...loaded.model ? { model: loaded.model } : {} };
|
|
26273
|
+
}
|
|
26274
|
+
async function resolveSessionModel(input) {
|
|
26275
|
+
const { argv, persistence, fallback, isKnownModel } = input;
|
|
26276
|
+
const windowOf = input.contextWindow ?? ((m) => resolveContextWindow2(m).window);
|
|
26277
|
+
const flagModel = hasValueFlag(argv, "model") ? flag(argv, "model", "") || void 0 : void 0;
|
|
26278
|
+
const resume = resumeFlag(argv);
|
|
26279
|
+
let chat;
|
|
26280
|
+
try {
|
|
26281
|
+
chat = await storedModelOf(resume, persistence);
|
|
26282
|
+
} catch {
|
|
26283
|
+
const model = flagModel ?? fallback;
|
|
26284
|
+
return { model, notice: `could not read this chat's model, so it will run on ${model}.` };
|
|
26285
|
+
}
|
|
26286
|
+
const stored = chat?.model;
|
|
26287
|
+
if (flagModel) {
|
|
26288
|
+
if (!chat || !stored || stored === flagModel) return { model: flagModel };
|
|
26289
|
+
const folds = estimateFill(chat.messages, windowOf(flagModel)) >= COMPACT_AT_FILL;
|
|
26290
|
+
const notice = `chat ${chat.id} was on ${stored}; continuing on ${flagModel} (this changes the chat's model)` + (folds ? ", and older history will be folded to fit." : ".");
|
|
26291
|
+
return { model: flagModel, notice };
|
|
26292
|
+
}
|
|
26293
|
+
if (stored && isKnownModel && !isKnownModel(stored)) {
|
|
26294
|
+
return { model: fallback, notice: `this chat used ${stored}, which is not available. It will run on ${fallback}.` };
|
|
26295
|
+
}
|
|
26296
|
+
return { model: resumeModel(void 0, stored, fallback) };
|
|
26297
|
+
}
|
|
26298
|
+
|
|
26216
26299
|
// src/shellTool.ts
|
|
26217
26300
|
import { spawn as spawn3 } from "node:child_process";
|
|
26218
26301
|
var MAX_OUTPUT = 3e4;
|
|
@@ -26323,7 +26406,7 @@ import { procTools } from "@corenel/harness/tools/procs";
|
|
|
26323
26406
|
|
|
26324
26407
|
// src/sidecarConnect.ts
|
|
26325
26408
|
import { stateDirName as stateDirName5 } from "@corenel/protocol";
|
|
26326
|
-
import { SidecarHost } from "@corenel/harness/sidecar/host";
|
|
26409
|
+
import { SidecarHost, isSidecarKeyMismatch, isSidecarAccountRefusal } from "@corenel/harness/sidecar/host";
|
|
26327
26410
|
|
|
26328
26411
|
// ../../node_modules/.pnpm/ws@8.21.0/node_modules/ws/wrapper.mjs
|
|
26329
26412
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -26486,12 +26569,209 @@ function hostBackedProcs(host, seed = []) {
|
|
|
26486
26569
|
};
|
|
26487
26570
|
}
|
|
26488
26571
|
|
|
26572
|
+
// src/sidecarDevice.ts
|
|
26573
|
+
init_tools_node();
|
|
26574
|
+
import { join as join10 } from "node:path";
|
|
26575
|
+
import { asEcPublicJwk, generatePrivateJwk, signerFromPrivateJwk } from "@corenel/harness/sidecar/deviceKeys";
|
|
26576
|
+
function cliDevicesPath(base) {
|
|
26577
|
+
return join10(corenelDir(base), "cli", "sidecar-devices.json");
|
|
26578
|
+
}
|
|
26579
|
+
var B64 = /^[A-Za-z0-9_-]{1,64}$/;
|
|
26580
|
+
var B43 = /^[A-Za-z0-9_-]{43}$/;
|
|
26581
|
+
function isCliDevice(v) {
|
|
26582
|
+
if (typeof v !== "object" || v === null) return false;
|
|
26583
|
+
const r = v;
|
|
26584
|
+
const k = r.privateJwk;
|
|
26585
|
+
return typeof r.id === "string" && B64.test(r.id) && typeof r.sidecarJkt === "string" && B43.test(r.sidecarJkt) && typeof k === "object" && k !== null && !!asEcPublicJwk({ kty: k.kty, crv: k.crv, x: k.x, y: k.y }) && typeof k.d === "string" && B43.test(k.d);
|
|
26586
|
+
}
|
|
26587
|
+
function parse2(text) {
|
|
26588
|
+
let raw;
|
|
26589
|
+
try {
|
|
26590
|
+
raw = JSON.parse(text);
|
|
26591
|
+
} catch {
|
|
26592
|
+
return null;
|
|
26593
|
+
}
|
|
26594
|
+
const r = raw;
|
|
26595
|
+
if (typeof r !== "object" || r === null || r.version !== 1) return null;
|
|
26596
|
+
if (typeof r.devices !== "object" || r.devices === null || Array.isArray(r.devices)) return null;
|
|
26597
|
+
const devices = /* @__PURE__ */ Object.create(null);
|
|
26598
|
+
for (const [url, d] of Object.entries(r.devices)) {
|
|
26599
|
+
if (!isCliDevice(d)) return null;
|
|
26600
|
+
const k = d.privateJwk;
|
|
26601
|
+
devices[url] = { id: d.id, sidecarJkt: d.sidecarJkt, privateJwk: { kty: k.kty, crv: k.crv, x: k.x, y: k.y, d: k.d } };
|
|
26602
|
+
}
|
|
26603
|
+
return { version: 1, devices };
|
|
26604
|
+
}
|
|
26605
|
+
function cliDeviceStore(path = cliDevicesPath()) {
|
|
26606
|
+
const file = fileTextStore(path);
|
|
26607
|
+
const dir = join10(path, "..");
|
|
26608
|
+
const load = async () => {
|
|
26609
|
+
const text = await file.read();
|
|
26610
|
+
return text === null ? { version: 1, devices: /* @__PURE__ */ Object.create(null) } : parse2(text);
|
|
26611
|
+
};
|
|
26612
|
+
const change = async (edit) => {
|
|
26613
|
+
const f = await load();
|
|
26614
|
+
if (!f) throw new Error(`${path} is not a file this CLI understands. Remove it to pair again.`);
|
|
26615
|
+
edit(f);
|
|
26616
|
+
await ensurePrivateDir(dir);
|
|
26617
|
+
await file.write(`${JSON.stringify(f, null, 2)}
|
|
26618
|
+
`);
|
|
26619
|
+
};
|
|
26620
|
+
return {
|
|
26621
|
+
async get(url) {
|
|
26622
|
+
try {
|
|
26623
|
+
const f = await load();
|
|
26624
|
+
return f && Object.prototype.hasOwnProperty.call(f.devices, url) ? f.devices[url] : void 0;
|
|
26625
|
+
} catch {
|
|
26626
|
+
return void 0;
|
|
26627
|
+
}
|
|
26628
|
+
},
|
|
26629
|
+
set: (url, device) => change((f) => {
|
|
26630
|
+
f.devices[url] = device;
|
|
26631
|
+
}),
|
|
26632
|
+
forget: (url) => change((f) => {
|
|
26633
|
+
delete f.devices[url];
|
|
26634
|
+
})
|
|
26635
|
+
};
|
|
26636
|
+
}
|
|
26637
|
+
async function sidecarCredential(url, env, store) {
|
|
26638
|
+
const kept = await store.get(url);
|
|
26639
|
+
const enroll = env.CORENEL_SIDECAR_CODE || void 0;
|
|
26640
|
+
if (!kept && !enroll) return null;
|
|
26641
|
+
const privateJwk = kept?.privateJwk ?? await generatePrivateJwk();
|
|
26642
|
+
return {
|
|
26643
|
+
...enroll ? { enroll } : {},
|
|
26644
|
+
...kept ? { device: { id: kept.id, sidecarJkt: kept.sidecarJkt } } : {},
|
|
26645
|
+
deviceKey: await signerFromPrivateJwk(privateJwk),
|
|
26646
|
+
privateJwk
|
|
26647
|
+
};
|
|
26648
|
+
}
|
|
26649
|
+
|
|
26650
|
+
// src/sidecarIdentity.ts
|
|
26651
|
+
function cliIdentity(o) {
|
|
26652
|
+
const fetchImpl = o.fetchImpl ?? fetch;
|
|
26653
|
+
const timeoutMs = o.timeoutMs ?? 8e3;
|
|
26654
|
+
let last = null;
|
|
26655
|
+
return {
|
|
26656
|
+
problem: () => last,
|
|
26657
|
+
answer: async ({ nonce, sidecarId, jkt, orgId }) => {
|
|
26658
|
+
last = null;
|
|
26659
|
+
let token;
|
|
26660
|
+
try {
|
|
26661
|
+
token = await o.token();
|
|
26662
|
+
} catch {
|
|
26663
|
+
token = null;
|
|
26664
|
+
}
|
|
26665
|
+
if (!token) {
|
|
26666
|
+
last = "this CLI is not signed in, and the sidecar only admits a verified account. Run `corenel login` (or set CORENEL_TOKEN), then try again.";
|
|
26667
|
+
return null;
|
|
26668
|
+
}
|
|
26669
|
+
try {
|
|
26670
|
+
const base = (await o.base()).replace(/\/+$/, "");
|
|
26671
|
+
const res = await fetchImpl(`${base}/v1/pairing-assertions`, {
|
|
26672
|
+
method: "POST",
|
|
26673
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
26674
|
+
body: JSON.stringify({ aud: sidecarId, nonce, jkt, ...orgId ? { org_id: orgId } : {} }),
|
|
26675
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
26676
|
+
});
|
|
26677
|
+
if (res.status === 403) {
|
|
26678
|
+
let code3;
|
|
26679
|
+
try {
|
|
26680
|
+
code3 = (await res.json())?.code;
|
|
26681
|
+
} catch {
|
|
26682
|
+
code3 = void 0;
|
|
26683
|
+
}
|
|
26684
|
+
if (code3 === "CLI_SCOPE") {
|
|
26685
|
+
last = "this CLI sign-in is not allowed to connect to sidecars. Run `corenel login` again and allow sidecar access when you approve it.";
|
|
26686
|
+
return null;
|
|
26687
|
+
}
|
|
26688
|
+
if (orgId) {
|
|
26689
|
+
last = `your account is not a member of the organization this sidecar admits (${orgId}).`;
|
|
26690
|
+
return null;
|
|
26691
|
+
}
|
|
26692
|
+
}
|
|
26693
|
+
if (res.status === 401) {
|
|
26694
|
+
last = "the API refused this CLI sign-in. Run `corenel login` again, then try again.";
|
|
26695
|
+
return null;
|
|
26696
|
+
}
|
|
26697
|
+
if (!res.ok) {
|
|
26698
|
+
last = `could not get a pairing assertion from ${base} (HTTP ${res.status}).`;
|
|
26699
|
+
return null;
|
|
26700
|
+
}
|
|
26701
|
+
const body = await res.json();
|
|
26702
|
+
const assertion = body?.assertion;
|
|
26703
|
+
if (typeof assertion !== "string" || !assertion) {
|
|
26704
|
+
last = `${base} answered without a pairing assertion.`;
|
|
26705
|
+
return null;
|
|
26706
|
+
}
|
|
26707
|
+
return assertion;
|
|
26708
|
+
} catch (e) {
|
|
26709
|
+
last = `could not reach the API for a pairing assertion (${e instanceof Error ? e.message : String(e)}).`;
|
|
26710
|
+
return null;
|
|
26711
|
+
}
|
|
26712
|
+
}
|
|
26713
|
+
};
|
|
26714
|
+
}
|
|
26715
|
+
|
|
26489
26716
|
// src/sidecarConnect.ts
|
|
26717
|
+
init_tools_node();
|
|
26718
|
+
import { hostname } from "node:os";
|
|
26490
26719
|
var CONNECT_TIMEOUT_MS = 3e3;
|
|
26491
|
-
|
|
26720
|
+
var HANDSHAKE_TIMEOUT_MS = 15e3;
|
|
26721
|
+
function cliDeviceName(host = hostname) {
|
|
26722
|
+
let h = "";
|
|
26723
|
+
try {
|
|
26724
|
+
h = host().trim();
|
|
26725
|
+
} catch {
|
|
26726
|
+
h = "";
|
|
26727
|
+
}
|
|
26728
|
+
return h ? `CLI on ${h}` : "CLI";
|
|
26729
|
+
}
|
|
26730
|
+
function defaultCliIdentity(env) {
|
|
26731
|
+
return cliIdentity({ base: () => apiBase(void 0, { env }), token: () => nodeAuthToken().getToken() });
|
|
26732
|
+
}
|
|
26733
|
+
async function reportHandshakeFailure(e, url, usedDevice, store, identity2, fallback) {
|
|
26734
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
26735
|
+
if (isSidecarKeyMismatch(e)) {
|
|
26736
|
+
process.stderr.write(`corenel: sidecar connection at ${url} failed to complete: ${msg} -- ${fallback}
|
|
26737
|
+
`);
|
|
26738
|
+
process.stderr.write(mismatchNote(url));
|
|
26739
|
+
return;
|
|
26740
|
+
}
|
|
26741
|
+
if (isSidecarAccountRefusal(e)) {
|
|
26742
|
+
const problem = identity2.problem() ?? "the sidecar verified this account and did not admit it (not its owner, or not a member of its organization, or a device another member paired).";
|
|
26743
|
+
process.stderr.write(`corenel: the sidecar at ${url} did not accept this CLI: ${problem} -- ${fallback}
|
|
26744
|
+
`);
|
|
26745
|
+
return;
|
|
26746
|
+
}
|
|
26747
|
+
process.stderr.write(`corenel: sidecar connection at ${url} failed to complete: ${msg} -- ${fallback}
|
|
26748
|
+
`);
|
|
26749
|
+
if (usedDevice && /denied/.test(msg)) await store.forget(url).catch(() => void 0);
|
|
26750
|
+
}
|
|
26751
|
+
async function credentialOptions(url, env, store) {
|
|
26752
|
+
const cred = await sidecarCredential(url, env, store);
|
|
26753
|
+
if (!cred) return null;
|
|
26754
|
+
const { privateJwk, ...host } = cred;
|
|
26755
|
+
return {
|
|
26756
|
+
...host,
|
|
26757
|
+
usedDevice: !!cred.device,
|
|
26758
|
+
onDeviceIssued: (device) => {
|
|
26759
|
+
store.set(url, { ...device, privateJwk }).catch((e) => {
|
|
26760
|
+
process.stderr.write(`corenel: paired, but could not keep the device key: ${e instanceof Error ? e.message : String(e)}
|
|
26761
|
+
`);
|
|
26762
|
+
});
|
|
26763
|
+
}
|
|
26764
|
+
};
|
|
26765
|
+
}
|
|
26766
|
+
function mismatchNote(url) {
|
|
26767
|
+
return `corenel: the sidecar at ${url} is not the sidecar this CLI paired with (its key does not match the pinned fingerprint). Check the address. If that sidecar was reinstalled, pair again with a fresh code (CORENEL_SIDECAR_CODE from \`corenel-sidecar pair\`).
|
|
26768
|
+
`;
|
|
26769
|
+
}
|
|
26770
|
+
async function connectSidecarProcs(env = process.env, store = cliDeviceStore(), identity2 = defaultCliIdentity(env)) {
|
|
26492
26771
|
const url = env.CORENEL_SIDECAR_URL;
|
|
26493
|
-
|
|
26494
|
-
|
|
26772
|
+
if (!url) return null;
|
|
26773
|
+
const cred = await credentialOptions(url, env, store);
|
|
26774
|
+
if (!cred) return null;
|
|
26495
26775
|
const ac = new AbortController();
|
|
26496
26776
|
const timer = setTimeout(() => ac.abort(), CONNECT_TIMEOUT_MS);
|
|
26497
26777
|
let channel;
|
|
@@ -26506,24 +26786,23 @@ async function connectSidecarProcs(env = process.env) {
|
|
|
26506
26786
|
} finally {
|
|
26507
26787
|
clearTimeout(timer);
|
|
26508
26788
|
}
|
|
26789
|
+
const { usedDevice, ...credential } = cred;
|
|
26509
26790
|
const host = new SidecarHost({
|
|
26510
26791
|
channel,
|
|
26511
|
-
|
|
26792
|
+
...credential,
|
|
26512
26793
|
stateDir: stateDirName5(),
|
|
26513
26794
|
clientId: `corenel-cli-${process.pid}`,
|
|
26514
|
-
clientName:
|
|
26795
|
+
clientName: cliDeviceName(),
|
|
26796
|
+
onIdentityChallenge: identity2.answer,
|
|
26515
26797
|
providers: {},
|
|
26516
26798
|
registerTool: () => {
|
|
26517
26799
|
}
|
|
26518
26800
|
});
|
|
26519
26801
|
try {
|
|
26520
|
-
await host.connect(
|
|
26802
|
+
await host.connect(HANDSHAKE_TIMEOUT_MS);
|
|
26521
26803
|
} catch (e) {
|
|
26522
|
-
process.stderr.write(
|
|
26523
|
-
`corenel: sidecar connection at ${url} failed to complete: ${e instanceof Error ? e.message : String(e)} -- continuing without proc_start/proc_stop/proc_list.
|
|
26524
|
-
`
|
|
26525
|
-
);
|
|
26526
26804
|
host.disconnect("handshake failed");
|
|
26805
|
+
await reportHandshakeFailure(e, url, usedDevice, store, identity2, "continuing without proc_start/proc_stop/proc_list.");
|
|
26527
26806
|
return null;
|
|
26528
26807
|
}
|
|
26529
26808
|
if (!host.supportsProcs()) {
|
|
@@ -26533,10 +26812,11 @@ async function connectSidecarProcs(env = process.env) {
|
|
|
26533
26812
|
const seed = await host.procList().catch(() => []);
|
|
26534
26813
|
return { procs: hostBackedProcs(host, seed), disconnect: () => host.disconnect() };
|
|
26535
26814
|
}
|
|
26536
|
-
async function connectMcpAdmin(env = process.env) {
|
|
26815
|
+
async function connectMcpAdmin(env = process.env, store = cliDeviceStore(), identity2 = defaultCliIdentity(env)) {
|
|
26537
26816
|
const url = env.CORENEL_SIDECAR_URL;
|
|
26538
|
-
|
|
26539
|
-
|
|
26817
|
+
if (!url) return null;
|
|
26818
|
+
const cred = await credentialOptions(url, env, store);
|
|
26819
|
+
if (!cred) return null;
|
|
26540
26820
|
const ac = new AbortController();
|
|
26541
26821
|
const timer = setTimeout(() => ac.abort(), CONNECT_TIMEOUT_MS);
|
|
26542
26822
|
let channel;
|
|
@@ -26551,24 +26831,23 @@ async function connectMcpAdmin(env = process.env) {
|
|
|
26551
26831
|
} finally {
|
|
26552
26832
|
clearTimeout(timer);
|
|
26553
26833
|
}
|
|
26834
|
+
const { usedDevice, ...credential } = cred;
|
|
26554
26835
|
const host = new SidecarHost({
|
|
26555
26836
|
channel,
|
|
26556
|
-
|
|
26837
|
+
...credential,
|
|
26557
26838
|
stateDir: stateDirName5(),
|
|
26558
26839
|
clientId: `corenel-cli-${process.pid}`,
|
|
26559
|
-
clientName:
|
|
26840
|
+
clientName: cliDeviceName(),
|
|
26841
|
+
onIdentityChallenge: identity2.answer,
|
|
26560
26842
|
providers: {},
|
|
26561
26843
|
registerTool: () => {
|
|
26562
26844
|
}
|
|
26563
26845
|
});
|
|
26564
26846
|
try {
|
|
26565
|
-
await host.connect(
|
|
26847
|
+
await host.connect(HANDSHAKE_TIMEOUT_MS);
|
|
26566
26848
|
} catch (e) {
|
|
26567
|
-
process.stderr.write(
|
|
26568
|
-
`corenel: sidecar connection at ${url} failed to complete: ${e instanceof Error ? e.message : String(e)} -- writing MCP config locally instead.
|
|
26569
|
-
`
|
|
26570
|
-
);
|
|
26571
26849
|
host.disconnect("handshake failed");
|
|
26850
|
+
await reportHandshakeFailure(e, url, usedDevice, store, identity2, "writing MCP config locally instead.");
|
|
26572
26851
|
return null;
|
|
26573
26852
|
}
|
|
26574
26853
|
if (!host.supportsMcpAdmin()) {
|
|
@@ -26821,9 +27100,9 @@ async function resolveStrategyPrompt(files, id) {
|
|
|
26821
27100
|
}
|
|
26822
27101
|
|
|
26823
27102
|
// src/policyResolve.ts
|
|
26824
|
-
|
|
26825
|
-
import {
|
|
26826
|
-
import { join as
|
|
27103
|
+
init_tools_node();
|
|
27104
|
+
import { readFile as readFile2, stat } from "node:fs/promises";
|
|
27105
|
+
import { join as join11 } from "node:path";
|
|
26827
27106
|
import { stateDirName as stateDirName6 } from "@corenel/protocol";
|
|
26828
27107
|
import { BUILTIN_POLICIES, STANDARD_POLICY as STANDARD_POLICY3, policyFromYaml } from "@corenel/harness/guardrail";
|
|
26829
27108
|
var PolicyResolutionError = class extends Error {
|
|
@@ -26871,25 +27150,55 @@ function validateOrThrow(text, path) {
|
|
|
26871
27150
|
}
|
|
26872
27151
|
return result.policy;
|
|
26873
27152
|
}
|
|
27153
|
+
async function projectPolicyPath(cwd, homeBase) {
|
|
27154
|
+
return join11(corenelDir(homeBase), "policies", `ws-${await workspaceApprovalsId(cwd)}.yaml`);
|
|
27155
|
+
}
|
|
27156
|
+
async function fileExists(path) {
|
|
27157
|
+
try {
|
|
27158
|
+
await stat(path);
|
|
27159
|
+
return true;
|
|
27160
|
+
} catch (e) {
|
|
27161
|
+
return !(isErrnoException(e) && e.code === "ENOENT");
|
|
27162
|
+
}
|
|
27163
|
+
}
|
|
26874
27164
|
async function resolvePolicy(opts) {
|
|
26875
|
-
const { flag: flag2, cwd } = opts;
|
|
27165
|
+
const { flag: flag2, cwd, homeBase } = opts;
|
|
27166
|
+
const warnings = [];
|
|
26876
27167
|
if (flag2) {
|
|
26877
27168
|
if (Object.prototype.hasOwnProperty.call(BUILTIN_POLICIES, flag2)) {
|
|
26878
|
-
return { policy: BUILTIN_POLICIES[flag2], source: { kind: "builtin", name: flag2 } };
|
|
27169
|
+
return { policy: BUILTIN_POLICIES[flag2], source: { kind: "builtin", name: flag2 }, warnings };
|
|
26879
27170
|
}
|
|
26880
27171
|
const policy = await loadRequiredPolicyFile(
|
|
26881
27172
|
flag2,
|
|
26882
27173
|
`corenel: unknown --policy "${flag2}" -- not a builtin (${Object.keys(BUILTIN_POLICIES).join(", ")}) and no file exists at that path.`
|
|
26883
27174
|
);
|
|
26884
|
-
return { policy, source: { kind: "file", path: flag2 } };
|
|
27175
|
+
return { policy, source: { kind: "file", path: flag2 }, warnings };
|
|
27176
|
+
}
|
|
27177
|
+
const stateDir = corenelDir(homeBase);
|
|
27178
|
+
const projectPath = await projectPolicyPath(cwd, homeBase);
|
|
27179
|
+
const userPath = join11(stateDir, "policy.yaml");
|
|
27180
|
+
if (await insideAnyRoot([cwd], stateDir) !== null) {
|
|
27181
|
+
const present2 = [];
|
|
27182
|
+
if (await fileExists(projectPath)) present2.push(projectPath);
|
|
27183
|
+
if (await fileExists(userPath)) present2.push(userPath);
|
|
27184
|
+
if (present2.length) {
|
|
27185
|
+
throw new PolicyResolutionError(
|
|
27186
|
+
`corenel: not loading ${present2.join(" or ")}: ${stateDir} is inside this folder (${cwd}), where the agent's file tools could have written it. Start corenel from a folder that does not contain it, or pass --policy <name|path> explicitly.`
|
|
27187
|
+
);
|
|
27188
|
+
}
|
|
27189
|
+
return { policy: STANDARD_POLICY3, source: { kind: "default" }, warnings };
|
|
27190
|
+
}
|
|
27191
|
+
const workspacePath = join11(cwd, stateDirName6(), "policy.yaml");
|
|
27192
|
+
if (await fileExists(workspacePath)) {
|
|
27193
|
+
warnings.push(
|
|
27194
|
+
`corenel: ${workspacePath} ignored -- a policy inside the workspace can be written by the agent or arrive in a cloned repo. To use it for this folder, move it to ${projectPath}, or pass --policy ${workspacePath} explicitly.`
|
|
27195
|
+
);
|
|
26885
27196
|
}
|
|
26886
|
-
const projectPath = join10(cwd, stateDirName6(), "policy.yaml");
|
|
26887
27197
|
const project = await loadDiscoveredPolicyFile(projectPath);
|
|
26888
|
-
if (project) return { policy: project, source: { kind: "project", path: projectPath } };
|
|
26889
|
-
const userPath = join10(homedir3(), stateDirName6(), "policy.yaml");
|
|
27198
|
+
if (project) return { policy: project, source: { kind: "project", path: projectPath }, warnings };
|
|
26890
27199
|
const user = await loadDiscoveredPolicyFile(userPath);
|
|
26891
|
-
if (user) return { policy: user, source: { kind: "user", path: userPath } };
|
|
26892
|
-
return { policy: STANDARD_POLICY3, source: { kind: "default" } };
|
|
27200
|
+
if (user) return { policy: user, source: { kind: "user", path: userPath }, warnings };
|
|
27201
|
+
return { policy: STANDARD_POLICY3, source: { kind: "default" }, warnings };
|
|
26893
27202
|
}
|
|
26894
27203
|
|
|
26895
27204
|
// ../mcp/src/httpClient.ts
|
|
@@ -27362,7 +27671,7 @@ function isExpired(cred, nowMs) {
|
|
|
27362
27671
|
}
|
|
27363
27672
|
|
|
27364
27673
|
// ../mcp/src/auth.ts
|
|
27365
|
-
import { DEFAULT_API_BASE as
|
|
27674
|
+
import { DEFAULT_API_BASE as DEFAULT_API_BASE4 } from "@corenel/protocol";
|
|
27366
27675
|
function safeOrigin(url) {
|
|
27367
27676
|
try {
|
|
27368
27677
|
return new URL(url).origin;
|
|
@@ -27370,24 +27679,30 @@ function safeOrigin(url) {
|
|
|
27370
27679
|
return "its URL";
|
|
27371
27680
|
}
|
|
27372
27681
|
}
|
|
27373
|
-
function isCorenelRelayUrl(url, bases) {
|
|
27682
|
+
function isCorenelRelayUrl(url, bases, devApiBases = []) {
|
|
27374
27683
|
let u;
|
|
27375
27684
|
try {
|
|
27376
27685
|
u = new URL(url);
|
|
27377
27686
|
} catch {
|
|
27378
27687
|
return false;
|
|
27379
27688
|
}
|
|
27380
|
-
if (u.protocol !== "https:") return false;
|
|
27381
27689
|
if (!/\/mcp\/hosted\/[^/]+\/?$/.test(u.pathname)) return false;
|
|
27382
|
-
|
|
27690
|
+
const sameOrigin = (b) => {
|
|
27383
27691
|
try {
|
|
27384
27692
|
return new URL(b).origin === u.origin;
|
|
27385
27693
|
} catch {
|
|
27386
27694
|
return false;
|
|
27387
27695
|
}
|
|
27388
|
-
}
|
|
27696
|
+
};
|
|
27697
|
+
if (u.protocol === "https:") return [...bases, ...devApiBases].some(sameOrigin);
|
|
27698
|
+
if (u.protocol === "http:" && isLoopbackHost(u.hostname)) return devApiBases.some(sameOrigin);
|
|
27699
|
+
return false;
|
|
27700
|
+
}
|
|
27701
|
+
function isLoopbackHost(hostname2) {
|
|
27702
|
+
const h = hostname2.replace(/^\[|\]$/g, "").toLowerCase();
|
|
27703
|
+
return h === "localhost" || h === "127.0.0.1" || h === "::1";
|
|
27389
27704
|
}
|
|
27390
|
-
function authProviderFor(stateDir, server, fetchImpl = fetch, accountToken = async () => null, relayBases = async () => []) {
|
|
27705
|
+
function authProviderFor(stateDir, server, fetchImpl = fetch, accountToken = async () => null, relayBases = async () => [], devApiBases = []) {
|
|
27391
27706
|
async function doRefresh(cred) {
|
|
27392
27707
|
if (!cred.refreshToken) {
|
|
27393
27708
|
throw new Error(`"${server.name}" has no refresh token on file -- run: corenel mcp login ${server.name}`);
|
|
@@ -27400,7 +27715,7 @@ function authProviderFor(stateDir, server, fetchImpl = fetch, accountToken = asy
|
|
|
27400
27715
|
return {
|
|
27401
27716
|
async headers() {
|
|
27402
27717
|
const cred = await readMcpCredential(stateDir, server.name);
|
|
27403
|
-
if (server.hosted && !isCorenelRelayUrl(server.url, [
|
|
27718
|
+
if (server.hosted && !isCorenelRelayUrl(server.url, [DEFAULT_API_BASE4, ...await relayBases()], devApiBases)) {
|
|
27404
27719
|
throw new Error(`"${server.name}" is marked hosted but ${safeOrigin(server.url)} is not a Corenel relay; refusing to send the account token there.`);
|
|
27405
27720
|
}
|
|
27406
27721
|
if (!cred && !server.hosted) {
|
|
@@ -27451,10 +27766,17 @@ async function accountTokenFor(stateDir) {
|
|
|
27451
27766
|
return null;
|
|
27452
27767
|
}
|
|
27453
27768
|
}
|
|
27769
|
+
function mcpClientFactory(opts = {}) {
|
|
27770
|
+
const devApiBases = [...opts.devApiBases ?? []];
|
|
27771
|
+
return (stateDir, server) => buildClient(stateDir, server, devApiBases);
|
|
27772
|
+
}
|
|
27454
27773
|
function defaultClientFactory(stateDir, server) {
|
|
27774
|
+
return buildClient(stateDir, server, []);
|
|
27775
|
+
}
|
|
27776
|
+
function buildClient(stateDir, server, devApiBases) {
|
|
27455
27777
|
return new McpHttpClient(
|
|
27456
27778
|
server,
|
|
27457
|
-
authProviderFor(stateDir, server, timedFetch(AUTH_REQUEST_TIMEOUT_MS), () => accountTokenFor(stateDir), () => relayBasesFor(stateDir))
|
|
27779
|
+
authProviderFor(stateDir, server, timedFetch(AUTH_REQUEST_TIMEOUT_MS), () => accountTokenFor(stateDir), () => relayBasesFor(stateDir), devApiBases)
|
|
27458
27780
|
);
|
|
27459
27781
|
}
|
|
27460
27782
|
async function relayBasesFor(stateDir) {
|
|
@@ -27747,9 +28069,9 @@ function gatedAgentTools(registry, policy) {
|
|
|
27747
28069
|
// ../mcp/src/cache.ts
|
|
27748
28070
|
import { createHash as createHash2 } from "node:crypto";
|
|
27749
28071
|
import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3, rename as rename2 } from "node:fs/promises";
|
|
27750
|
-
import { dirname as dirname8, join as
|
|
28072
|
+
import { dirname as dirname8, join as join12 } from "node:path";
|
|
27751
28073
|
function toolCachePath(stateDir) {
|
|
27752
|
-
return
|
|
28074
|
+
return join12(stateDir, "mcp-tools.json");
|
|
27753
28075
|
}
|
|
27754
28076
|
function credentialFingerprint(cred) {
|
|
27755
28077
|
if (!cred) return "none";
|
|
@@ -27792,11 +28114,11 @@ function cacheEntryFor(cache, server, cred) {
|
|
|
27792
28114
|
}
|
|
27793
28115
|
|
|
27794
28116
|
// src/mcpRegistry.ts
|
|
27795
|
-
async function openCliMcp(stateDir, policy) {
|
|
28117
|
+
async function openCliMcp(stateDir, policy, opts = {}) {
|
|
27796
28118
|
try {
|
|
27797
28119
|
const servers = await readMcpServers(stateDir);
|
|
27798
28120
|
if (servers.length === 0) return null;
|
|
27799
|
-
const registry = new McpRegistry(stateDir);
|
|
28121
|
+
const registry = new McpRegistry(stateDir, mcpClientFactory({ devApiBases: opts.devApiBases ?? [] }));
|
|
27800
28122
|
const cache = await readToolCache(stateDir);
|
|
27801
28123
|
const usable = {};
|
|
27802
28124
|
for (const s of servers) {
|
|
@@ -28189,8 +28511,18 @@ import { lookup as dnsLookup } from "node:dns/promises";
|
|
|
28189
28511
|
import { setOutboundResolver } from "@corenel/harness/tools/urlGuard";
|
|
28190
28512
|
var EXIT_POLICY_PARK_REFUSED = 3;
|
|
28191
28513
|
var EXIT_BUDGET_EXCEEDED = 4;
|
|
28192
|
-
|
|
28193
|
-
|
|
28514
|
+
function replSessionId(id) {
|
|
28515
|
+
return id === UNATTACHED_SESSION ? null : id;
|
|
28516
|
+
}
|
|
28517
|
+
function replCompaction(client, model, contextWindow, files, getSessionId) {
|
|
28518
|
+
return {
|
|
28519
|
+
summarize: makeSummarizer(client, model, { contextWindow }),
|
|
28520
|
+
storage: makeSessionFoldStorage({ getFiles: () => files, root: sessionsRoot(), sessionId: getSessionId })
|
|
28521
|
+
};
|
|
28522
|
+
}
|
|
28523
|
+
async function agentSetup(argv, chosenModel) {
|
|
28524
|
+
const gatewayBase = await resolveCliApiBase(flag(argv, "base", ""));
|
|
28525
|
+
setGatewayBase2(gatewayBase.base);
|
|
28194
28526
|
const token = await nodeAuthToken().getToken();
|
|
28195
28527
|
if (!token) {
|
|
28196
28528
|
process.stderr.write("corenel: not signed in.\n Run `corenel login`, or set CORENEL_TOKEN=<token> (a gateway bearer token), then try again.\n");
|
|
@@ -28232,19 +28564,22 @@ async function agentSetup(argv) {
|
|
|
28232
28564
|
let policy;
|
|
28233
28565
|
let policySource;
|
|
28234
28566
|
try {
|
|
28235
|
-
|
|
28567
|
+
const resolved = await resolvePolicy({
|
|
28236
28568
|
...hasValueFlag(argv, "policy") ? { flag: flag(argv, "policy", "") } : {},
|
|
28237
28569
|
cwd: process.cwd()
|
|
28238
|
-
})
|
|
28570
|
+
});
|
|
28571
|
+
({ policy, source: policySource } = resolved);
|
|
28572
|
+
for (const w of resolved.warnings) process.stderr.write(`${w}
|
|
28573
|
+
`);
|
|
28239
28574
|
} catch (e) {
|
|
28240
28575
|
sidecarProcs?.disconnect();
|
|
28241
28576
|
throw e;
|
|
28242
28577
|
}
|
|
28243
|
-
const mcp = await openCliMcp(configWorkspaceRoot(), policy);
|
|
28578
|
+
const mcp = await openCliMcp(configWorkspaceRoot(), policy, { devApiBases: cliDevRelayBases(gatewayBase) });
|
|
28244
28579
|
if (mcp) tools.push(...mcp.tools);
|
|
28245
28580
|
return {
|
|
28246
28581
|
client: createChatClient2({ getToken: async () => token }),
|
|
28247
|
-
model: flag(argv, "model",
|
|
28582
|
+
model: chosenModel ?? flag(argv, "model", DEFAULT_MODEL2),
|
|
28248
28583
|
system,
|
|
28249
28584
|
tools,
|
|
28250
28585
|
identity: identity2,
|
|
@@ -28348,10 +28683,10 @@ async function run(argv) {
|
|
|
28348
28683
|
* with a real window buys exactly that awareness without turning on
|
|
28349
28684
|
* rung-1 offload here (this path builds no recall store).
|
|
28350
28685
|
*
|
|
28351
|
-
* No
|
|
28686
|
+
* No fold storage: a one-shot run has no session to write one beside.
|
|
28352
28687
|
* It still compacts — surviving the wall matters more than recording why. */
|
|
28353
|
-
compression: { mode: "off", contextWindow:
|
|
28354
|
-
compaction: { summarize: makeSummarizer(client, model) },
|
|
28688
|
+
compression: { mode: "off", contextWindow: resolveContextWindow3(model).window },
|
|
28689
|
+
compaction: { summarize: makeSummarizer(client, model, { contextWindow: resolveContextWindow3(model).window }) },
|
|
28355
28690
|
// A spawned sub-agent runs under the SAME gate as the parent turn -- never
|
|
28356
28691
|
// looser, so it can't be used to route around the default-deny above.
|
|
28357
28692
|
toolCtx: nodeToolCtx(ac.signal, {
|
|
@@ -28389,7 +28724,12 @@ async function run(argv) {
|
|
|
28389
28724
|
}
|
|
28390
28725
|
}
|
|
28391
28726
|
async function interactive(argv) {
|
|
28392
|
-
const
|
|
28727
|
+
const chosen = await resolveSessionModel({
|
|
28728
|
+
argv,
|
|
28729
|
+
persistence: createSessionPersistence(new NodeFileService(process.cwd())),
|
|
28730
|
+
fallback: DEFAULT_MODEL2
|
|
28731
|
+
});
|
|
28732
|
+
const { client, model, tools, identity: identity2, policy, policySource, memory, mcp, disconnect } = await agentSetup(argv, chosen.model);
|
|
28393
28733
|
try {
|
|
28394
28734
|
await runInteractive();
|
|
28395
28735
|
} finally {
|
|
@@ -28404,7 +28744,7 @@ async function interactive(argv) {
|
|
|
28404
28744
|
const approvalsContext = { runId: randomUUID(), workspace: basename5(cwd) };
|
|
28405
28745
|
let modelRates = null;
|
|
28406
28746
|
let priceTable = /* @__PURE__ */ new Map();
|
|
28407
|
-
const resolvedWindow =
|
|
28747
|
+
const resolvedWindow = resolveContextWindow3(model);
|
|
28408
28748
|
alog("context window", model, resolvedWindow.window, `(${resolvedWindow.source})`);
|
|
28409
28749
|
const compression = { mode: "balanced", contextWindow: resolvedWindow.window, strategy: "deferred" };
|
|
28410
28750
|
const priceBase = await apiBase(flag(argv, "base", ""));
|
|
@@ -28472,11 +28812,9 @@ async function interactive(argv) {
|
|
|
28472
28812
|
const onSessionId = (id) => {
|
|
28473
28813
|
currentSessionId = id;
|
|
28474
28814
|
};
|
|
28475
|
-
const compaction =
|
|
28476
|
-
|
|
28477
|
-
|
|
28478
|
-
};
|
|
28479
|
-
const resume = resumeFlag(argv);
|
|
28815
|
+
const compaction = replCompaction(client, model, () => compression.contextWindow, files, () => replSessionId(currentSessionId));
|
|
28816
|
+
const resumeBase = resumeFlag(argv);
|
|
28817
|
+
const resume = resumeBase ? { ...resumeBase, ...chosen.notice ? { notice: chosen.notice } : {} } : void 0;
|
|
28480
28818
|
if (chooseFace(currentFaceEnv(argv)) === "tui") {
|
|
28481
28819
|
try {
|
|
28482
28820
|
const { runTui } = await import("@corenel/term/ink");
|
|
@@ -28575,6 +28913,7 @@ async function interactive(argv) {
|
|
|
28575
28913
|
}
|
|
28576
28914
|
async function main() {
|
|
28577
28915
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
28916
|
+
setSourceRun(isSourceRun(import.meta.url));
|
|
28578
28917
|
setOutboundResolver(async (h) => (await dnsLookup(h, { all: true, verbatim: true })).map((a) => a.address));
|
|
28579
28918
|
try {
|
|
28580
28919
|
setAgentLogging(hasBooleanFlag(process.argv, "debug") || process.env.CORENEL_DEBUG === "1");
|
|
@@ -28652,7 +28991,7 @@ Logged in. Token saved to ${authTokenPath()}
|
|
|
28652
28991
|
await runLogout();
|
|
28653
28992
|
break;
|
|
28654
28993
|
case "doctor": {
|
|
28655
|
-
process.exitCode = await runDoctor(void 0, void 0, flag(rest, "model",
|
|
28994
|
+
process.exitCode = await runDoctor(void 0, void 0, flag(rest, "model", DEFAULT_MODEL2), resolveDoctorEnv(rest));
|
|
28656
28995
|
break;
|
|
28657
28996
|
}
|
|
28658
28997
|
case "approvals":
|
|
@@ -28725,5 +29064,7 @@ main().catch((e) => {
|
|
|
28725
29064
|
export {
|
|
28726
29065
|
EXIT_BUDGET_EXCEEDED,
|
|
28727
29066
|
EXIT_POLICY_PARK_REFUSED,
|
|
28728
|
-
buildRunPermission
|
|
29067
|
+
buildRunPermission,
|
|
29068
|
+
replCompaction,
|
|
29069
|
+
replSessionId
|
|
28729
29070
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corenel/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Corenel CLI — runs the harness in node, in-proc, no transport. The headless proof the kernel is host-agnostic. (corenel run/ask/chat/login + start --sidecar to follow.)",
|
|
6
6
|
"bin": {
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"openai": "^4.77.0",
|
|
11
11
|
"ws": "^8.18.0",
|
|
12
|
-
"@corenel/config": "0.
|
|
13
|
-
"@corenel/harness": "0.
|
|
14
|
-
"@corenel/mcp": "0.
|
|
15
|
-
"@corenel/protocol": "0.
|
|
16
|
-
"@corenel/term": "0.
|
|
12
|
+
"@corenel/config": "0.6.1",
|
|
13
|
+
"@corenel/harness": "0.14.0",
|
|
14
|
+
"@corenel/mcp": "0.5.0",
|
|
15
|
+
"@corenel/protocol": "0.13.0",
|
|
16
|
+
"@corenel/term": "0.8.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"esbuild": "^0.21.5",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"tsx": "^4.19.2",
|
|
23
23
|
"typescript": "^5.9.3",
|
|
24
24
|
"@corenel/crew": "0.0.0",
|
|
25
|
-
"@corenel/tools-node": "0.
|
|
25
|
+
"@corenel/tools-node": "0.7.1"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|