clearotron 0.3.0-beta.4 → 0.3.0-beta.5
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/INSTALL.md +2 -0
- package/bin/onboard.mjs +42 -10
- package/bin/start.mjs +56 -16
- package/build-info.json +2 -2
- package/driver/CHANGELOG.md +19 -10
- package/driver/engine/openai-agent.mjs +52 -2
- package/driver/package.json +1 -1
- package/driver/portal-upstream.mjs +9 -0
- package/driver/suite-census.json +29 -5
- package/mcp-server/CHANGELOG.md +8 -2
- package/mcp-server/lib/options.mjs +4 -1
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
- package/portal-ui/dist/assets/{index-DWYCsOCJ.js → index-DBUVdQT-.js} +6 -0
- package/portal-ui/dist/index.html +1 -1
- package/portal-ui/package.json +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/scripts/engine-probe.mjs +2 -2
package/INSTALL.md
CHANGED
|
@@ -749,6 +749,8 @@ Use the demo to see what this system produces. Use `npx clearotron start` to run
|
|
|
749
749
|
|
|
750
750
|
- Generates `PORTAL_SECRET` and `TRADEMARK_MCP_TOKEN_SECRET` and **appends** them to `~/.config/clearotron/.env` at
|
|
751
751
|
mode 600. Append, never rewrite: that file also holds the credentials `npx clearotron install` collected.
|
|
752
|
+
It also records where saved searches are kept, `CLEAROTRON_RECIPES_DIR` and `RECIPE_REPO_ROOT`, so
|
|
753
|
+
`clearotron doctor` and a connected assistant read the same saved searches as the portal.
|
|
752
754
|
- Creates `~/trademark/` — `pool/`, `workspace/`, `queue/`, `outbox/`, `locks/`, an empty grants file,
|
|
753
755
|
and a small git repository for saved searches. Same base directory `npx clearotron install` uses, so whichever
|
|
754
756
|
of the two you ran first, the other finds the same install. Move it with `npx clearotron start --base <dir>`.
|
package/bin/onboard.mjs
CHANGED
|
@@ -99,7 +99,15 @@ import { entrypointOf } from "../driver/systemd/install-census.mjs"; //
|
|
|
99
99
|
import { overlayReport, renderOverlayReport } from "../shared/doctrine-overlay.mjs"; // — the doctor reports the overlay
|
|
100
100
|
import { whereSavesGo, storeCommitRefusal, storeInRepo, storeOutsideRepoMessage, resolveStoreRepoRoot } from "../shared/store-in-repo.mjs"; // — doctor says where a portal save goes once it is committed, and why saved searches are off
|
|
101
101
|
import { engineInventory, engineMode, ENGINE_MODES } from "../driver/config-inventory.mjs"; //
|
|
102
|
-
import { probeEngineTurn, probeFailureText,
|
|
102
|
+
import { probeEngineTurn, probeFailureText, PROBE_TIMEOUT_SEC, engineEnvKeys } from "../driver/engine/probe.mjs";
|
|
103
|
+
|
|
104
|
+
// THE PROVING SENTENCES NAME NO MODEL. They printed the driver's tier word, which is an Anthropic model's
|
|
105
|
+
// name, on both engines, so a codex user was told setup was about to spend on a model family they do not
|
|
106
|
+
// use. "Its cheapest model" is true of either engine and brands neither.
|
|
107
|
+
export const probingLine = (engineId) =>
|
|
108
|
+
`Probing ${engineId} with one turn on its cheapest model (this SPENDS; ${PROBE_TIMEOUT_SEC}s ceiling)…`;
|
|
109
|
+
export const proveQuestion = ({ engineId, lane }) =>
|
|
110
|
+
`Prove ${engineId} on the ${lane} lane now with one turn on its cheapest model (a few tokens, ${PROBE_TIMEOUT_SEC}s ceiling)?`;
|
|
103
111
|
import { runRequiredNames, missingRequirements, REGISTER_ENV, ENGINE_ENV } from "../driver/run-requirements.mjs"; // the order-time gate's own question, asked here rather than restated
|
|
104
112
|
import { pinEnv, envFrom } from "../shared/env-aliases.mjs";
|
|
105
113
|
import { isEntrypoint } from "../shared/is-entrypoint.mjs"; // — one entry-point test, all spellings
|
|
@@ -1206,7 +1214,7 @@ export async function runCheck() {
|
|
|
1206
1214
|
// deliberate — a second reader would drift from this one exactly as the composer and the checker did
|
|
1207
1215
|
// in F41, and the drift is invisible because both sides keep passing their own arms.
|
|
1208
1216
|
const unitDir = join(homedir(), ".config", "systemd", "user");
|
|
1209
|
-
const { BACKGROUND_UNITS } = await import(pathToFileURL(join(REPO, "bin", "start.mjs")).href);
|
|
1217
|
+
const { BACKGROUND_UNITS, startPaths } = await import(pathToFileURL(join(REPO, "bin", "start.mjs")).href);
|
|
1210
1218
|
const hosted = BACKGROUND_UNITS.some((u) => existsSync(join(unitDir, u)));
|
|
1211
1219
|
const unitEnv = hosted
|
|
1212
1220
|
? unitEnvironment({
|
|
@@ -1440,7 +1448,7 @@ export async function runCheck() {
|
|
|
1440
1448
|
} else if (!bin.executable || bin.relative) {
|
|
1441
1449
|
info("not probed — there is no usable binary to probe. Fix the line above first.");
|
|
1442
1450
|
} else {
|
|
1443
|
-
say(`\n
|
|
1451
|
+
say(`\n ${probingLine(engineId)}`);
|
|
1444
1452
|
// The engine as a RUN would see it: environment first, .env behind it. Only the engine-selection
|
|
1445
1453
|
// keys — the probe must bill exactly the way this box bills and moves no other variable.
|
|
1446
1454
|
const probeEnv = { ...process.env };
|
|
@@ -1628,6 +1636,13 @@ export async function runCheck() {
|
|
|
1628
1636
|
// because a reader debugging `clearotron run` by hand IS this process.
|
|
1629
1637
|
info(`this command's own process ${where} — a CLI is not started by the units' EnvironmentFile, so `
|
|
1630
1638
|
+ "that differs by design and is not what a run uses");
|
|
1639
|
+
} else if (!hosted && svcCustomers?.from === serviceEnvLabel) {
|
|
1640
|
+
// NAMED IN THE ENV FILE ALONE, ON AN INSTALL WITH NO UNITS. `clearotron start` loads that file and
|
|
1641
|
+
// its services inherit the store; this command does not load it, so its own process fell back to
|
|
1642
|
+
// the bundled roster and printed that as the answer, one line below a ✓ for the same variable.
|
|
1643
|
+
ok(`the services resolve profiles from ${svcCustomers.v} (${svcCustomers.from})`);
|
|
1644
|
+
info(`this command's own process ${where} — it does not load ${serviceEnvFile}, so that differs by `
|
|
1645
|
+
+ `design and is not what \`${invoke("start")}\` hands the services`);
|
|
1631
1646
|
} else {
|
|
1632
1647
|
if (r.situation === "overlay" && !r.findings.length) ok(`${where} — the configured store`);
|
|
1633
1648
|
else if (r.situation === "bundled-fallback") info(`${where} — THE BUNDLED DEMO ROSTER, because CLEAROTRON_CUSTOMERS_DIR is unset. Legitimate on a generic-defaults install; a fallback either way, and it is what a misconfigured deployment also looks like`);
|
|
@@ -1638,7 +1653,8 @@ export async function runCheck() {
|
|
|
1638
1653
|
info(`the units are installed but their environment could not be read (${unitEnv?.why ?? "no reason given"}) — `
|
|
1639
1654
|
+ "what the services resolve is not judged here, and the line above is this process's own answer");
|
|
1640
1655
|
info("what a RUN used is its own `profile-store` journal line — this command reports what the "
|
|
1641
|
-
+ (hosted ? "units' file says, which the running services read at their own start"
|
|
1656
|
+
+ (hosted ? "units' file says, which the running services read at their own start"
|
|
1657
|
+
: `environment you are typing in and your environment file say, which \`${invoke("start")}\` reads at its own start`));
|
|
1642
1658
|
// ── AND WHO IS ACTUALLY IN IT ─────────────────────────────────────────
|
|
1643
1659
|
//
|
|
1644
1660
|
// The line above names the STORE. An operator who has just configured one wants to know their
|
|
@@ -1807,16 +1823,32 @@ export async function runCheck() {
|
|
|
1807
1823
|
// be read, the lines above already said so and nothing is judged here. A store that is configured but
|
|
1808
1824
|
// holds a file that cannot be read fails every company's saved searches, in the portal and the
|
|
1809
1825
|
// connector alike, so that is read here too.
|
|
1826
|
+
//
|
|
1827
|
+
// WITH NO UNITS, THE SERVICES ARE `clearotron start`'s CHILDREN, and it hands every one of them a store
|
|
1828
|
+
// whether or not the env file names it. So "off" is only ever true of a hosted box. This read the env
|
|
1829
|
+
// file alone, and on a local install started before `start` wrote the store there, it told a portal
|
|
1830
|
+
// that was listing saved searches that they were off.
|
|
1810
1831
|
if (!hosted || serviceKnown) {
|
|
1811
|
-
|
|
1832
|
+
// Layered as effectiveForService layers it — this command's environment over the services' file — so
|
|
1833
|
+
// the repository root is read from the same place the store directory is.
|
|
1834
|
+
let storeEnv = { ...(serviceFileEnv ?? {}), ...process.env };
|
|
1835
|
+
let recipesSet = effectiveForService("CLEAROTRON_RECIPES_DIR");
|
|
1836
|
+
let handedBy = "";
|
|
1837
|
+
if (!hosted && !recipesSet?.v) {
|
|
1838
|
+
const handed = startPaths({ env: storeEnv });
|
|
1839
|
+
recipesSet = { v: handed.recipes, from: "clearotron start", name: "CLEAROTRON_RECIPES_DIR" };
|
|
1840
|
+
storeEnv = { ...storeEnv, RECIPE_REPO_ROOT: handed.configStore };
|
|
1841
|
+
handedBy = ` — where \`${invoke("start")}\` puts them`;
|
|
1842
|
+
}
|
|
1812
1843
|
const recipesDir = recipesSet?.v || null;
|
|
1813
1844
|
if (!recipesDir) {
|
|
1814
1845
|
info("saved searches are off: CLEAROTRON_RECIPES_DIR is not set, so Custom searches in the portal and the "
|
|
1815
1846
|
+ "connector offer none. Name a directory inside a git repository to switch them on");
|
|
1847
|
+
} else if (handedBy && !existsSync(join(storeEnv.RECIPE_REPO_ROOT, ".git"))) {
|
|
1848
|
+
// NOT CREATED YET IS NOT UNREADABLE. The store does not exist until the first start makes it, and
|
|
1849
|
+
// reading it now would report a missing directory as a broken one.
|
|
1850
|
+
info(`saved searches switch on at the first \`${invoke("start")}\`, which creates their store in ${recipesDir}`);
|
|
1816
1851
|
} else {
|
|
1817
|
-
// Layered as effectiveForService layers it — this command's environment over the services' file — so
|
|
1818
|
-
// the repository root is read from the same place the store directory above was.
|
|
1819
|
-
const storeEnv = { ...(serviceFileEnv ?? {}), ...process.env };
|
|
1820
1852
|
const resolved = resolveStoreRepoRoot({ names: ["RECIPE_REPO_ROOT", "PROFILE_REPO_ROOT"], fallback: REPO, env: storeEnv });
|
|
1821
1853
|
const reach = storeInRepo(recipesDir, resolved.root);
|
|
1822
1854
|
if (!reach.ok) {
|
|
@@ -1829,7 +1861,7 @@ export async function runCheck() {
|
|
|
1829
1861
|
if (unreadable) {
|
|
1830
1862
|
warn(`saved searches cannot be read from ${recipesDir}: ${unreadable}. Every company's saved searches fail `
|
|
1831
1863
|
+ "to load, in the portal and the connector, until that file is fixed");
|
|
1832
|
-
} else ok(`saved searches are read from ${recipesDir}, and saves are committed in ${reach.repo}`);
|
|
1864
|
+
} else ok(`saved searches are read from ${recipesDir}${handedBy}, and saves are committed in ${reach.repo}`);
|
|
1833
1865
|
}
|
|
1834
1866
|
}
|
|
1835
1867
|
}
|
|
@@ -3281,7 +3313,7 @@ try {
|
|
|
3281
3313
|
// skipping it costs an hour later.
|
|
3282
3314
|
info("A file that exists is not an engine that works — so setup tries one before writing anything.");
|
|
3283
3315
|
info("It takes a few seconds here. Skipped, a broken engine surfaces an hour into a real search.");
|
|
3284
|
-
if (!await confirm(
|
|
3316
|
+
if (!await confirm(proveQuestion({ engineId: pick.id, lane: authPick.id }), true)) {
|
|
3285
3317
|
info("Not proven, so not written. Pick again — the last row configures no engine at all.");
|
|
3286
3318
|
continue;
|
|
3287
3319
|
}
|
package/bin/start.mjs
CHANGED
|
@@ -355,6 +355,50 @@ export function defaultGrantsPath({ env = process.env, demo = false } = {}) {
|
|
|
355
355
|
return installPaths(join(homedir(), demo ? "trademark-demo" : "trademark")).grants;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
/**
|
|
359
|
+
* The paths `clearotron start` hands its services: the install's layout under `base`, with whatever the
|
|
360
|
+
* environment already names winning over the layout's default, as `start` has always applied it.
|
|
361
|
+
*
|
|
362
|
+
* EXPORTED FOR THE ONE OTHER READER THAT MUST AGREE WITH IT. On an install with no background units,
|
|
363
|
+
* `clearotron doctor` has no unit file to read the services' environment from: the services are this
|
|
364
|
+
* command's children and get the saved-search store from here. An install started before the store was
|
|
365
|
+
* written to the env file has no line there to read, so doctor asks this function for it, and the
|
|
366
|
+
* answer it prints and the one the services were given have one author.
|
|
367
|
+
*
|
|
368
|
+
* NOT IN A DEMO. Nothing the environment says about an install is the demo's: with the reader's settings
|
|
369
|
+
* in force, 0.3.0-beta.1's demo seeded its example reports into their real archive.
|
|
370
|
+
*/
|
|
371
|
+
export function startPaths({ env = process.env, base = join(homedir(), "trademark"), demo = false } = {}) {
|
|
372
|
+
const paths = installPaths(base);
|
|
373
|
+
if (demo) return paths;
|
|
374
|
+
for (const [k, name] of [["pool", "CLEAROTRON_REPORTS_DIR"], ["workspace", "CLEAROTRON_WORK_DIR"], ["queue", "CLEAROTRON_QUEUE_DIR"],
|
|
375
|
+
["outbox", "CLEAROTRON_OUTBOX_DIR"], ["locks", "CLEAROTRON_RUN_LOCK_DIR"], ["grants", "CLEAROTRON_ACCESS_FILE"],
|
|
376
|
+
["recipes", "CLEAROTRON_RECIPES_DIR"]]) if (env[name]) paths[k] = env[name];
|
|
377
|
+
if (env.RECIPE_REPO_ROOT) paths.configStore = env.RECIPE_REPO_ROOT;
|
|
378
|
+
if (env.PORTAL_AUDIT) paths.audit = env.PORTAL_AUDIT;
|
|
379
|
+
return paths;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* The saved-search store, as the env file must record it for every reader OUTSIDE this command's tree.
|
|
384
|
+
*
|
|
385
|
+
* The services are handed the store by `childEnv`. A connector an assistant launches, `clearotron doctor`
|
|
386
|
+
* and every other hand-run command are not this command's children: they read the env file, and the
|
|
387
|
+
* file never named the store. So on a local install the portal listed a company's saved searches, the
|
|
388
|
+
* connected assistant was told there were none, and doctor said saved searches were off. Written
|
|
389
|
+
* add-only beside the secrets, as `npx clearotron install` writes the reports and work directories: a
|
|
390
|
+
* line an operator wrote wins.
|
|
391
|
+
*/
|
|
392
|
+
export function storesForOtherReaders(paths) {
|
|
393
|
+
return { CLEAROTRON_RECIPES_DIR: paths.recipes, RECIPE_REPO_ROOT: paths.configStore };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Written above each line, so a reader who moves the install sees that these two move with it.
|
|
397
|
+
const STORE_NOTES = {
|
|
398
|
+
CLEAROTRON_RECIPES_DIR: "Where saved searches are kept. `clearotron start` created it; edit both lines if you move the install.",
|
|
399
|
+
RECIPE_REPO_ROOT: "The git repository saved searches are committed in, which holds the directory above.",
|
|
400
|
+
};
|
|
401
|
+
|
|
358
402
|
/** Everything this install keeps on disk, under one base directory. */
|
|
359
403
|
export function installPaths(base) {
|
|
360
404
|
return {
|
|
@@ -846,20 +890,11 @@ if (isMain) {
|
|
|
846
890
|
const DEMO = argv.includes("--demo");
|
|
847
891
|
// The same base `npm run setup` writes under, so whichever of the two a reader ran first, the other
|
|
848
892
|
// finds the same install rather than a second one beside it.
|
|
849
|
-
const paths = installPaths(flag("--base", join(homedir(), DEMO ? "trademark-demo" : "trademark")));
|
|
850
893
|
// Whatever the environment already says wins over the base-derived default, for every path — a reader
|
|
851
|
-
// who ran `npm run setup` has these in .env already and this must not move their data.
|
|
852
|
-
//
|
|
853
|
-
//
|
|
854
|
-
|
|
855
|
-
// rather than deleted from the environment, so a real start cannot be reached by this branch at all.
|
|
856
|
-
if (!DEMO) {
|
|
857
|
-
for (const [k, name] of [["pool", "CLEAROTRON_REPORTS_DIR"], ["workspace", "CLEAROTRON_WORK_DIR"], ["queue", "CLEAROTRON_QUEUE_DIR"],
|
|
858
|
-
["outbox", "CLEAROTRON_OUTBOX_DIR"], ["locks", "CLEAROTRON_RUN_LOCK_DIR"], ["grants", "CLEAROTRON_ACCESS_FILE"],
|
|
859
|
-
["recipes", "CLEAROTRON_RECIPES_DIR"]]) if (process.env[name]) paths[k] = process.env[name];
|
|
860
|
-
if (process.env.RECIPE_REPO_ROOT) paths.configStore = process.env.RECIPE_REPO_ROOT;
|
|
861
|
-
if (process.env.PORTAL_AUDIT) paths.audit = process.env.PORTAL_AUDIT;
|
|
862
|
-
}
|
|
894
|
+
// who ran `npm run setup` has these in .env already and this must not move their data. Not in a demo,
|
|
895
|
+
// which `startPaths` says why. One author, because `doctor` asks the same function what the services
|
|
896
|
+
// were handed.
|
|
897
|
+
const paths = startPaths({ env: process.env, base: flag("--base", join(homedir(), DEMO ? "trademark-demo" : "trademark")), demo: DEMO });
|
|
863
898
|
// ── THIS INSTALL'S FIRST START, read before this start writes either file that answers it ────────────
|
|
864
899
|
//
|
|
865
900
|
// The grants file and the config store's repository are both written further down, on every start
|
|
@@ -1117,14 +1152,19 @@ if (isMain) {
|
|
|
1117
1152
|
const portalSecret = secretFor("PORTAL_SECRET");
|
|
1118
1153
|
const tokenSecret = secretFor("TRADEMARK_MCP_TOKEN_SECRET");
|
|
1119
1154
|
if (!process.env.PORTAL_LOCAL_USER) generated.PORTAL_LOCAL_USER = user;
|
|
1155
|
+
const stores = DEMO ? {} : storesForOtherReaders(paths);
|
|
1120
1156
|
|
|
1121
1157
|
// A DEMO WRITES NO SECRETS AND NO ADDRESS. They are generated per run and live in memory only, which
|
|
1122
1158
|
// is the same posture the ops key already has here — and it is what makes "removing the demo is one
|
|
1123
1159
|
// directory" true rather than nearly true.
|
|
1124
|
-
|
|
1160
|
+
//
|
|
1161
|
+
// THE STORES ARE WRITTEN EVEN WHEN NO SECRET IS. An install whose secrets are already in the file is
|
|
1162
|
+
// exactly the install whose connector and doctor could not see its saved searches, and a gate on the
|
|
1163
|
+
// secrets alone would never reach it.
|
|
1164
|
+
if ((Object.keys(generated).length || Object.keys(stores).length) && !DEMO) {
|
|
1125
1165
|
let existing = "";
|
|
1126
1166
|
try { existing = readFileSync(ENV_PATH, "utf8"); } catch (e) { if (e.code !== "ENOENT") fatal(`${ENV_PATH} exists but could not be read (${e.code}).`); }
|
|
1127
|
-
const merged = mergeEnvFile(existing, generated);
|
|
1167
|
+
const merged = mergeEnvFile(existing, { ...generated, ...stores }, { notes: STORE_NOTES });
|
|
1128
1168
|
if (merged.added.length) {
|
|
1129
1169
|
// ONE WRITER FOR EVERY FILE THAT HOLDS CREDENTIALS, and it creates the directory. This site had its
|
|
1130
1170
|
// own copy of the write and did not learn what the wizard's copy learned when `.env` moved under
|
|
@@ -1135,7 +1175,7 @@ if (isMain) {
|
|
|
1135
1175
|
} catch (e) {
|
|
1136
1176
|
fatal(`could not write ${ENV_PATH} (${String(e?.message ?? e)}).`);
|
|
1137
1177
|
}
|
|
1138
|
-
// NAMES only.
|
|
1178
|
+
// NAMES only. Some of what this file holds is credentials.
|
|
1139
1179
|
say(` wrote ${merged.added.join(", ")} to ${ENV_PATH} (mode 600)`);
|
|
1140
1180
|
}
|
|
1141
1181
|
}
|
package/build-info.json
CHANGED
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.3.0-beta.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5f7e295: Fixed: A Clearotron install signed in to OpenAI's `codex` with a subscription keeps working after codex refreshes its login. Before, every search after the first refresh failed within seconds until you signed in again.
|
|
8
|
+
- 2878809: Fixed: When the company store cannot record a new company, the New company page now says why and what fixes it, instead of "Try again shortly". Someone who does not run the installation is told to ask whoever does.
|
|
9
|
+
- e7cd9e1: Fixed: On an install run with `clearotron start`, a connected assistant now lists the same saved searches as the portal, including Generic's. `clearotron doctor` no longer says working saved searches are off, and it names one place profiles come from.
|
|
10
|
+
- e7cd9e1: Fixed: When setup offers to try your engine, it no longer names an Anthropic model to someone who chose OpenAI's `codex`.
|
|
11
|
+
|
|
3
12
|
## 0.3.0-beta.4
|
|
4
13
|
|
|
5
14
|
### Minor Changes
|
|
@@ -23,9 +32,9 @@
|
|
|
23
32
|
- a782aad: Fixed: Creating a company is refused, with nothing left behind, when the configuration store cannot record it. The company used to be created anyway, with no record of who made it or when, and its organisation was given access to it.
|
|
24
33
|
|
|
25
34
|
A store with no git identity is the usual cause on a new machine, and the refusal names the command that fixes it. Setup and `clearotron start` now check a store they adopt for this straight away.
|
|
26
|
-
- d1ef225: Fixed: A
|
|
35
|
+
- d1ef225: Fixed: A search step stopped at its time limit now records the output it actually produced. It used to record a small fraction, so a step that was working read as one that had stalled.
|
|
27
36
|
|
|
28
|
-
The token totals `clearotron tokens` reports for runs with a stopped
|
|
37
|
+
The token totals `clearotron tokens` reports for runs with a stopped step now include that output.
|
|
29
38
|
- 0ff42d1: Fixed: `clearotron doctor` now says when saved searches are switched off and why, and when a saved search file cannot be read.
|
|
30
39
|
|
|
31
40
|
An assistant asking for saved searches is told when they could not be read, instead of being told there are none.
|
|
@@ -79,11 +88,11 @@
|
|
|
79
88
|
|
|
80
89
|
Fixed: A search now records any of your default territories that the engine cannot search, in the record of that search. A mistyped default no longer narrows a search silently.
|
|
81
90
|
- eacfce4: Fixed: An assistant connected to a local install now sees the saved searches the portal shows, and can plan a run from one of them. Before, outside the demo, only the portal was told where saved searches are kept, so an assistant was told the install had none.
|
|
82
|
-
- 34cc1c7: For operators: A health check that could not
|
|
91
|
+
- 34cc1c7: For operators: A deployment health check that could not finish now fails instead of reporting success. Two parts of the service check could skip themselves. One skipped when the installation did not say which installation it is; the other when the scan of the service files did not finish. Both used to note that they had not run and then pass.
|
|
83
92
|
|
|
84
93
|
For operators: Set `CLEAROTRON_BOX` to `prod` or `test`. Those are the only two values the check accepts; anything else, including any other name, reads as unnamed and fails. The failure names the setting and says what went unchecked.
|
|
85
94
|
|
|
86
|
-
For operators: The
|
|
95
|
+
For operators: The part that could skip itself is the one that notices a service that has stopped and stayed stopped. The other part lists what is running, so it cannot see something that is no longer there. While the first part is skipped, a service can disappear without the check saying anything.
|
|
87
96
|
- b45a5cf: Fixed: `clearotron doctor` now reports everything a search would be refused for, checked against the environment the search will run in. So an installation it passes is one that can run a search.
|
|
88
97
|
|
|
89
98
|
Fixed: A token set in the installation's own configuration file now reaches the engine check. A headless server set up the documented way proves its engine instead of reporting it signed out.
|
|
@@ -128,7 +137,7 @@
|
|
|
128
137
|
Fixed: When a feature is switched off on your installation, the screen says so and what to change. It used to suggest trying again shortly.
|
|
129
138
|
|
|
130
139
|
Fixed: The link to the risk-framework guide opens in a new tab and goes straight to the section on writing your own. It also appears on the Company profile screen.
|
|
131
|
-
- ad088d6: Fixed: The audit workbook's "What was searched" sheet is now in plain words. Its Result and Note columns carry the search log's own notes.
|
|
140
|
+
- ad088d6: Fixed: The audit workbook's "What was searched" sheet is now in plain words. Its Result and Note columns carry the search log's own notes. Internal terms could reach them: a status such as "deferred", a full web address, a bare HTTP code. Those words are now replaced as the workbook is built, a republished report included. A search recorded as not run still reads as not searched. Search terms and names stay exactly as written, because they record what was searched.
|
|
132
141
|
- ff16a3b: For operators: The deployment health check now reports whether the component that updates an installation is itself up to date. It was the one part of a deployment the check could not identify. An installation kept current by an out-of-date updater could report healthy while serving stale code.
|
|
133
142
|
|
|
134
143
|
For operators: An updater that cannot be identified is now reported as a failure rather than passed over. A copy old enough to predate this reporting writes nothing at all. That silence is the case worth knowing about, so it is treated as a finding.
|
|
@@ -158,7 +167,7 @@
|
|
|
158
167
|
|
|
159
168
|
The product now says company throughout. It used to say brand owner, account, client and customer for the same thing. The firm running the installation is named separately, in the top bar.
|
|
160
169
|
|
|
161
|
-
Companies created through the settings page were saved without a risk framework. Their matters were then rated under the
|
|
170
|
+
Companies created through the settings page were saved without a risk framework. Their matters were then rated under the default risk framework, with nothing on screen saying so. Every company created now carries one, and says which.
|
|
162
171
|
- f75266d: New: The setup wizard now asks for the organisation's name after the sign-in address, and the person who installs starts with access to everything.
|
|
163
172
|
|
|
164
173
|
`clearotron grant add` sets a person's two permissions with `--run` and `--manage`; with neither, the person can look and start nothing.
|
|
@@ -180,18 +189,18 @@
|
|
|
180
189
|
|
|
181
190
|
Getting the deck's shape wrong used to fail quietly. The profile screen showed the framework's title and your band colours, and silently omitted the box saying what the bands mean. The new command answers that question directly, using the screen's own read of the deck.
|
|
182
191
|
|
|
183
|
-
Fixed: a framework
|
|
192
|
+
Fixed: When you have a configuration store set and a risk framework comes from the product's own files instead, the product now says so.
|
|
184
193
|
|
|
185
194
|
Your store is looked in first, and the product's files answer when it is silent. The product ships decks under names you may also have chosen. So a deck that went missing from your store was replaced by ours rather than reported absent. Same band words, different rubric, nothing raised anywhere. The profile screen now writes one line naming what happened, and the new command reports it.
|
|
186
195
|
|
|
187
|
-
Fixed:
|
|
196
|
+
Fixed: The built-in triage framework's profile page explains its bands again.
|
|
188
197
|
|
|
189
198
|
Its band sections stated their meanings as plain paragraphs, which the screen does not read. Every company without a framework of its own saw band colours and no explanation. The wording is unchanged.
|
|
190
|
-
- cfb9a9f: Fixed: `clearotron doctor` now
|
|
199
|
+
- cfb9a9f: Fixed: `clearotron doctor` now lists the companies your portal's key may start runs for.
|
|
191
200
|
|
|
192
201
|
The key carries a list of the companies it may start runs for. A company added after the key was minted is outside it. Doctor reported the key's expiry and never its coverage. So the one command whose job is to tell you what a machine is configured for said nothing about it.
|
|
193
202
|
|
|
194
|
-
It reads the
|
|
203
|
+
It reads the company list the background services read, not the one a command run in a terminal would find, and it says which. The two can differ, and when they do, that difference is the answer.
|
|
195
204
|
|
|
196
205
|
The line is a note, not a failure. Your portal takes a fresh credential at the start of every call, so a company outside the key is not normally refused. It is refused when the portal cannot take a fresh one, and the line says so and gives you the command to widen the key.
|
|
197
206
|
- ba2899b: Fixed: On reports searched through Signa, each register finding now links to the office's own page for that record, where the office publishes one. Singapore publishes no such page, so its registrations are cited by number, and the report says why. A number that an office's page cannot take is cited the same way.
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
// CLEAROTRON_CODEX_SANDBOX_BYPASS=1 swaps that `--sandbox workspace-write` for
|
|
23
23
|
// `--dangerously-bypass-approvals-and-sandbox` — see buildCodexArgs below for why.
|
|
24
24
|
|
|
25
|
-
import { mkdtempSync, writeFileSync, copyFileSync, existsSync, rmSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
25
|
+
import { mkdtempSync, writeFileSync, copyFileSync, existsSync, rmSync, readFileSync, readdirSync, statSync, symlinkSync, lstatSync, realpathSync } from "node:fs";
|
|
26
|
+
import { writeSecretFile } from "../../shared/secret-file.mjs"; // the rotated login goes back the way every credential is written
|
|
26
27
|
import { tmpdir, homedir } from "node:os";
|
|
27
28
|
import { join } from "node:path";
|
|
28
29
|
import { runStreamingChild, absolutizeSkillRefs, WRITE_DISCIPLINE, buildEnvelope, resolveSpawnCwd } from "./common.mjs";
|
|
@@ -174,6 +175,49 @@ export function spawnEnv(base = process.env, codexHome) {
|
|
|
174
175
|
|
|
175
176
|
const authFilePath = (env = process.env) => env.CLEAROTRON_OPENAI_AUTH_FILE || join(homedir(), ".codex", "auth.json");
|
|
176
177
|
|
|
178
|
+
// ── THE LOGIN IS THE MASTER'S, NOT A COPY'S ─────────────────────────────────────────────────────────
|
|
179
|
+
//
|
|
180
|
+
// codex refreshes its access token inside a turn and rotates the refresh token as it does, and the
|
|
181
|
+
// provider accepts each refresh token once. The stage home used to be seeded with a COPY of the master
|
|
182
|
+
// login and deleted when its ladder settled, so the rotated token was deleted with it. The master kept a
|
|
183
|
+
// token the provider had already consumed, and every later stage on every run failed its refresh with a
|
|
184
|
+
// 401 until a person signed in again. The engine's only report was an exit code.
|
|
185
|
+
//
|
|
186
|
+
// So the stage home LINKS to the master: a refresh codex writes in place lands where the next stage
|
|
187
|
+
// reads, including a ladder running beside this one. If codex replaces the link with a file of its own,
|
|
188
|
+
// `returnAuth` writes that file back.
|
|
189
|
+
|
|
190
|
+
/** Point `<codexHome>/auth.json` at the master login. Returns what the master held, for `returnAuth`. */
|
|
191
|
+
function seedAuth(masterPath, codexHome) {
|
|
192
|
+
const seat = join(codexHome, "auth.json");
|
|
193
|
+
// A LINK LEFT BY THIS LADDER'S PREVIOUS TURN is removed first: copying the master onto a link to
|
|
194
|
+
// itself would truncate the one file the login lives in.
|
|
195
|
+
rmSync(seat, { force: true });
|
|
196
|
+
symlinkSync(masterPath, seat);
|
|
197
|
+
return readFileSync(masterPath, "utf8");
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* After a turn: a login codex rewrote as its own file goes back to the master.
|
|
202
|
+
*
|
|
203
|
+
* ONLY WHILE THE MASTER STILL HOLDS WHAT THIS TURN WAS SEEDED WITH. A master that moved meanwhile was
|
|
204
|
+
* rotated by another ladder, and the provider accepts one refresh per token, so of two rotations from
|
|
205
|
+
* one seed only one can be live. Writing ours over theirs could put back the spent one.
|
|
206
|
+
*/
|
|
207
|
+
export function returnAuth(masterPath, codexHome, seeded) { // @internal
|
|
208
|
+
const seat = join(codexHome, "auth.json");
|
|
209
|
+
let st;
|
|
210
|
+
try { st = lstatSync(seat); } catch { return false; }
|
|
211
|
+
if (st.isSymbolicLink() || !st.isFile()) return false; // written through the link, or not there
|
|
212
|
+
const now = readFileSync(seat, "utf8");
|
|
213
|
+
if (now === seeded) return false;
|
|
214
|
+
let master;
|
|
215
|
+
try { master = readFileSync(masterPath, "utf8"); } catch { return false; }
|
|
216
|
+
if (master !== seeded) return false;
|
|
217
|
+
writeSecretFile(masterPath, now);
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
|
|
177
221
|
// Build the codex argv. Prompt rides STDIN via the `-` placeholder (no MAX_ARG_STRLEN ceiling on a 150KB
|
|
178
222
|
// register prompt). Global flags precede the `resume` subcommand (flag-corpus ordering). config.toml
|
|
179
223
|
// (mcp_servers + developer_instructions) rides CODEX_HOME, not argv.
|
|
@@ -530,6 +574,7 @@ export const openaiAgentEngine = {
|
|
|
530
574
|
const ownHome = !providedHome;
|
|
531
575
|
try { codexHome = providedHome ?? mkdtempSync(join(tmpdir(), "codex-home-")); }
|
|
532
576
|
catch (e) { return errResult(t0, e, resumeRef); }
|
|
577
|
+
let authMaster = null, seeded = null;
|
|
533
578
|
try {
|
|
534
579
|
// Auth toggle (may throw loud on api-key-without-key — a config error, not a retryable failure).
|
|
535
580
|
const { env, mode } = spawnEnv(process.env, codexHome);
|
|
@@ -537,7 +582,9 @@ export const openaiAgentEngine = {
|
|
|
537
582
|
const af = authFilePath(process.env);
|
|
538
583
|
if (!existsSync(af))
|
|
539
584
|
throw new Error(`CLEAROTRON_AI_BILLING=subscription but no auth.json at ${af} — run \`codex login\` (or set CLEAROTRON_OPENAI_AUTH_FILE — NOT an aliased name, and deliberately left as it is), or use CLEAROTRON_AI_BILLING=api-key + CODEX_API_KEY.`);
|
|
540
|
-
|
|
585
|
+
// The REAL path, so a master that is itself a link is written where its login actually lives.
|
|
586
|
+
authMaster = realpathSync(af);
|
|
587
|
+
seeded = seedAuth(authMaster, codexHome);
|
|
541
588
|
}
|
|
542
589
|
// Per-run config.toml: the gather MCP servers (translated from the claude-shaped mcpConfig, so
|
|
543
590
|
// gather-config.mjs is untouched) + developer_instructions = WRITE_DISCIPLINE (codex's
|
|
@@ -574,6 +621,9 @@ export const openaiAgentEngine = {
|
|
|
574
621
|
if (!ev.model) ev.model = readServedModel(codexHome, spawnedAtMs);
|
|
575
622
|
return settleTuple({ r, ev, resumeRef });
|
|
576
623
|
} finally {
|
|
624
|
+
// BEFORE the home can be deleted, here or by the ladder that owns it. A failed write-back leaves the
|
|
625
|
+
// turn's result as it was; the next turn is seeded from the master either way.
|
|
626
|
+
try { if (authMaster) returnAuth(authMaster, codexHome, seeded); } catch { /* the master is unchanged */ }
|
|
577
627
|
try { if (ownHome && codexHome) rmSync(codexHome, { recursive: true, force: true }); } catch { /* best-effort cleanup */ }
|
|
578
628
|
}
|
|
579
629
|
},
|
package/driver/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "clearotron-driver",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.3.0-beta.
|
|
5
|
+
"version": "0.3.0-beta.5",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
7
7
|
"description": "Deterministic driver for the trademark clearance workflow: orchestration in code (fan-out, fan-in barrier, gating, retries); the model does judgment leaves only, through a reasoning CLI spawned per stage.",
|
|
8
8
|
"engines": {
|
|
@@ -420,6 +420,11 @@ export function makeUpstream({ callUpstream, callRecipes = null, recipesOff = nu
|
|
|
420
420
|
const draft = {};
|
|
421
421
|
for (const k of CREATABLE_FIELDS) if (body[k] !== undefined) draft[k] = body[k];
|
|
422
422
|
const r = await call("POST", "/profiles", draft, principal);
|
|
423
|
+
// A STORE THAT CANNOT RECORD THE COMPANY is answered with its path and the git commands that fix it.
|
|
424
|
+
// That sentence is for whoever runs the installation. A manager who holds an organisation cannot act
|
|
425
|
+
// on it and must not be shown the server's paths, so they are told what to do instead.
|
|
426
|
+
if (r.status === 409 && String(r.json?.code ?? "").startsWith("store_") && !seesEverything(principal))
|
|
427
|
+
return { status: 409, json: { code: r.json.code, error: STORE_REFUSAL_FOR_MANAGERS } };
|
|
423
428
|
if (r.status !== 201 || org == null || !fileCompany) return r;
|
|
424
429
|
const key = r.json?.key ?? r.json?.profile?.key ?? draft.key;
|
|
425
430
|
try { await fileCompany({ tenant: org, account: key }); }
|
|
@@ -431,6 +436,10 @@ export function makeUpstream({ callUpstream, callRecipes = null, recipesOff = nu
|
|
|
431
436
|
};
|
|
432
437
|
}
|
|
433
438
|
|
|
439
|
+
/** The store refusal as a manager who does not run the installation reads it: no path, no command. */
|
|
440
|
+
export const STORE_REFUSAL_FOR_MANAGERS = "No company was created: this installation cannot save new companies yet, "
|
|
441
|
+
+ "and nothing was changed. Ask whoever runs it to fix the company store.";
|
|
442
|
+
|
|
434
443
|
/**
|
|
435
444
|
* What a browser may state when creating a company.
|
|
436
445
|
*
|
package/driver/suite-census.json
CHANGED
|
@@ -135,6 +135,12 @@
|
|
|
135
135
|
"skips": 0,
|
|
136
136
|
"todos": 0
|
|
137
137
|
},
|
|
138
|
+
"a-codex-login-survives-its-own-refresh.test.mjs": {
|
|
139
|
+
"tests": 3,
|
|
140
|
+
"asserts": 14,
|
|
141
|
+
"skips": 0,
|
|
142
|
+
"todos": 0
|
|
143
|
+
},
|
|
138
144
|
"a-company-can-be-given-a-framework-after-it-exists.test.mjs": {
|
|
139
145
|
"tests": 8,
|
|
140
146
|
"asserts": 19,
|
|
@@ -381,6 +387,12 @@
|
|
|
381
387
|
"skips": 0,
|
|
382
388
|
"todos": 0
|
|
383
389
|
},
|
|
390
|
+
"a-local-install-hands-its-stores-to-every-reader.test.mjs": {
|
|
391
|
+
"tests": 1,
|
|
392
|
+
"asserts": 10,
|
|
393
|
+
"skips": 0,
|
|
394
|
+
"todos": 0
|
|
395
|
+
},
|
|
384
396
|
"a-mark-name-cannot-lie-about-how-it-reads.test.mjs": {
|
|
385
397
|
"tests": 9,
|
|
386
398
|
"asserts": 37,
|
|
@@ -747,6 +759,12 @@
|
|
|
747
759
|
"skips": 0,
|
|
748
760
|
"todos": 0
|
|
749
761
|
},
|
|
762
|
+
"a-store-refusal-reaches-the-person-who-can-fix-it.test.mjs": {
|
|
763
|
+
"tests": 3,
|
|
764
|
+
"asserts": 11,
|
|
765
|
+
"skips": 0,
|
|
766
|
+
"todos": 0
|
|
767
|
+
},
|
|
750
768
|
"a-stored-territory-that-does-nothing-is-named.test.mjs": {
|
|
751
769
|
"tests": 6,
|
|
752
770
|
"asserts": 12,
|
|
@@ -1804,8 +1822,8 @@
|
|
|
1804
1822
|
"todos": 0
|
|
1805
1823
|
},
|
|
1806
1824
|
"doctor-reads-the-units-not-the-shell.test.mjs": {
|
|
1807
|
-
"tests":
|
|
1808
|
-
"asserts":
|
|
1825
|
+
"tests": 32,
|
|
1826
|
+
"asserts": 93,
|
|
1809
1827
|
"skips": 0,
|
|
1810
1828
|
"todos": 0
|
|
1811
1829
|
},
|
|
@@ -4725,6 +4743,12 @@
|
|
|
4725
4743
|
"skips": 0,
|
|
4726
4744
|
"todos": 0
|
|
4727
4745
|
},
|
|
4746
|
+
"the-proving-question-names-no-engines-model.test.mjs": {
|
|
4747
|
+
"tests": 2,
|
|
4748
|
+
"asserts": 5,
|
|
4749
|
+
"skips": 0,
|
|
4750
|
+
"todos": 0
|
|
4751
|
+
},
|
|
4728
4752
|
"the-public-residue-is-a-floor.test.mjs": {
|
|
4729
4753
|
"tests": 15,
|
|
4730
4754
|
"asserts": 59,
|
|
@@ -5404,7 +5428,7 @@
|
|
|
5404
5428
|
},
|
|
5405
5429
|
"describe-options.test.mjs": {
|
|
5406
5430
|
"tests": 23,
|
|
5407
|
-
"asserts":
|
|
5431
|
+
"asserts": 99,
|
|
5408
5432
|
"skips": 0,
|
|
5409
5433
|
"todos": 0
|
|
5410
5434
|
},
|
|
@@ -5594,8 +5618,8 @@
|
|
|
5594
5618
|
"ext": "ts",
|
|
5595
5619
|
"perFile": {
|
|
5596
5620
|
"api.test.ts": {
|
|
5597
|
-
"tests":
|
|
5598
|
-
"asserts":
|
|
5621
|
+
"tests": 44,
|
|
5622
|
+
"asserts": 176,
|
|
5599
5623
|
"skips": 0,
|
|
5600
5624
|
"todos": 0
|
|
5601
5625
|
},
|
package/mcp-server/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# trademark-artifacts-mcp
|
|
2
2
|
|
|
3
|
+
## 0.3.0-beta.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e7cd9e1: Fixed: On an install run with `clearotron start`, a connected assistant now lists the same saved searches as the portal, including Generic's. `clearotron doctor` no longer says working saved searches are off, and it names one place profiles come from.
|
|
8
|
+
|
|
3
9
|
## 0.3.0-beta.4
|
|
4
10
|
|
|
5
11
|
No changes in this release.
|
|
@@ -18,11 +24,11 @@ No changes in this release.
|
|
|
18
24
|
|
|
19
25
|
### Patch Changes
|
|
20
26
|
|
|
21
|
-
- b8ec213: Fixed:
|
|
27
|
+
- b8ec213: Fixed: A company you create in the browser now appears in your assistant's list of companies straight away.
|
|
22
28
|
|
|
23
29
|
It used to appear only after the service restarted, although a search could already be started for it. Its projects were missing from the list in the same way, and so was its account in the search options.
|
|
24
30
|
|
|
25
|
-
The
|
|
31
|
+
The check comparing the running service with the configuration store no longer calls a company the portal's key does not cover a disagreement. It names that gap on a line of its own, with the command that widens the key.
|
|
26
32
|
|
|
27
33
|
## 0.2.4
|
|
28
34
|
|
|
@@ -193,7 +193,10 @@ function accountFor(key, { scope, now }) {
|
|
|
193
193
|
profile = loadProfiles().get(key) ?? null;
|
|
194
194
|
if (!profile) { fresh = loadProfiles({ force: true }); profile = fresh.get(key) ?? null; }
|
|
195
195
|
} catch { return null; } // unreadable roster ⇒ say nothing
|
|
196
|
-
|
|
196
|
+
// GENERIC IS ANSWERED LIKE ANY COMPANY (owner ruling, 2026-09-11). It owns saved searches in the portal,
|
|
197
|
+
// and a fresh install holds Generic alone, so treating it as nobody's account told the assistant on
|
|
198
|
+
// every such install that none existed.
|
|
199
|
+
if (!profile) return null;
|
|
197
200
|
|
|
198
201
|
let projects = [];
|
|
199
202
|
try {
|
package/mcp-server/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trademark-artifacts-mcp",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.5",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"private": true,
|
|
6
6
|
"description": "MCP server to interrogate clearotron trademark-clearance runs — list/read artifacts, trace the full decision flow, telemetry/cost, coverage, single-run search, and a gated single-step what-if. Imports the clearotron-driver read-only; touches no driver/template/deploy files.",
|
package/package.json
CHANGED
|
@@ -10309,6 +10309,12 @@ function decodeStatus(status, body) {
|
|
|
10309
10309
|
};
|
|
10310
10310
|
return { kind: "notFound" };
|
|
10311
10311
|
case 409: {
|
|
10312
|
+
const refused = refusalDetail(body);
|
|
10313
|
+
if (refused?.code.startsWith("store_")) return {
|
|
10314
|
+
kind: "reject",
|
|
10315
|
+
errors: errorsOf(body),
|
|
10316
|
+
detail: refused
|
|
10317
|
+
};
|
|
10312
10318
|
const msg = errorsOf(body)[0] ?? "This action could not be completed.";
|
|
10313
10319
|
return /version|conflict/i.test(msg) && !/confirmation|plan again|re-confirm/i.test(msg) ? {
|
|
10314
10320
|
kind: "conflict",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
-->
|
|
50
50
|
<link rel="preconnect" href="https://api.fontshare.com" crossorigin />
|
|
51
51
|
<link href="https://api.fontshare.com/v2/css?f[]=satoshi@400,500,700,900&display=swap" rel="stylesheet" />
|
|
52
|
-
<script type="module" crossorigin src="/portal/assets/index-
|
|
52
|
+
<script type="module" crossorigin src="/portal/assets/index-DBUVdQT-.js"></script>
|
|
53
53
|
<link rel="stylesheet" crossorigin href="/portal/assets/index-Cv-E_agg.css">
|
|
54
54
|
</head>
|
|
55
55
|
<body>
|
package/portal-ui/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "portal-ui",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.3.0-beta.
|
|
5
|
+
"version": "0.3.0-beta.5",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
7
7
|
"description": "The unified trademark portal UI. One address, one login: who you are decides what you see. Built as a static bundle, served by driver/portal-service.mjs — the browser never reaches profile-service or recipe-service.",
|
|
8
8
|
"engines": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trademark-oauth-mcp-bridge",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.5",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"private": true,
|
|
6
6
|
"description": "OAuth 2.1 MCP stdio bridge used by the engine's case-law gather stage (courtlistener / legaldatahunter).",
|
package/scripts/engine-probe.mjs
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
// env at module top — and every static import evaluates before this file's own body runs, so applying the
|
|
36
36
|
// aliases in the body would be too late for the capture it is meant to fix.
|
|
37
37
|
import "../shared/env-local.mjs";
|
|
38
|
-
import { probeEngineTurn, probeFailureText,
|
|
38
|
+
import { probeEngineTurn, probeFailureText, PROBE_TIMEOUT_SEC } from "../driver/engine/probe.mjs";
|
|
39
39
|
import { isEntrypoint } from "../shared/is-entrypoint.mjs"; // — realpath both sides, or a symlinked invocation exits 0 silently
|
|
40
40
|
|
|
41
41
|
const argv = process.argv.slice(2);
|
|
@@ -62,7 +62,7 @@ if (isEntrypoint(import.meta.url)) {
|
|
|
62
62
|
const env = engine ? { ...process.env, CLEAROTRON_AI: engine } : process.env;
|
|
63
63
|
const asJson = argv.includes("--json");
|
|
64
64
|
if (!asJson) {
|
|
65
|
-
console.error(`engine-probe: one
|
|
65
|
+
console.error(`engine-probe: one turn on the engine's cheapest model, up to ${PROBE_TIMEOUT_SEC}s, on this box's own `
|
|
66
66
|
+ `credential — real, and it costs whatever one cheap turn costs.`);
|
|
67
67
|
}
|
|
68
68
|
const verdict = await probeEngineTurn({ env });
|