@cortad/connector 0.6.3 → 0.6.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/README.md +3 -14
- package/lib/history/send.mjs +1 -2
- package/lib/intelligence-text.mjs +4 -11
- package/lib/intelligence.mjs +2 -5
- package/lib/read-text.mjs +8 -15
- package/local.mjs +9 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,8 +35,6 @@ cortad intelligence site <address> the one site the page key answers
|
|
|
35
35
|
cortad intelligence --wait return when the history is read, with what it found
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
The ingest key only sends conversations to Cortad: it reads nothing and spends nothing. It is never printed and never copied to a clipboard; the command names its line in your env file.
|
|
39
|
-
|
|
40
38
|
## Your coding agent
|
|
41
39
|
|
|
42
40
|
After the connect, your agent has six tools, one for each thing it needs from Cortad:
|
|
@@ -77,7 +75,6 @@ The hooks run after an edit and on your next message: the first names the prompt
|
|
|
77
75
|
-- <command> how your app starts; everything after -- is run as typed (--start "cmd" says the same)
|
|
78
76
|
--port 3000 use an app that is already running, as it is
|
|
79
77
|
--port 3000 -- <command> start a container and watch the port it publishes; the model base URL to set inside it is printed
|
|
80
|
-
--explain show what would be sent and started, then exit
|
|
81
78
|
--verbose print your app's output
|
|
82
79
|
--from <tool> intelligence history: the tool your conversations are read from
|
|
83
80
|
--stdin intelligence history: read your own export from standard input
|
|
@@ -88,23 +85,15 @@ The hooks run after an edit and on your next message: the first names the prompt
|
|
|
88
85
|
--wait intelligence: return when the history is read
|
|
89
86
|
```
|
|
90
87
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
## What leaves this machine
|
|
94
|
-
|
|
95
|
-
The part of the code that is its AI, chosen by the same parsers that read it: the prompts, the evals, the code that calls the model, the routes that reach it and the shapes they read, with the README, the manifests and the schema. The command prints the counts before it sends anything, and the list of files with the reason each was chosen is written to `~/.cortad/<project>/sent.json`. The commit history since the last connect goes too, each commit with its message, author, dates and a diff of those same files, with any line that carries a key replaced first. Env files are read only to hide their values; keys never leave. A project that connected before 2026-10-23 sent its whole tree and goes on doing so, so its read and its history stand.
|
|
96
|
-
|
|
97
|
-
After the connect, the command sends each AI endpoint one test request built from what was read, from this machine to your app on its port, and the conversations of a run go the same way. The exchanges, the model calls your app made and the replies are what Cortad judges; `--explain` prints what would be sent and started without sending anything.
|
|
98
|
-
|
|
99
|
-
With `intelligence`, the conversations you chose: the person's and the app's messages with their times, keys, card numbers, emails and phone numbers replaced on this machine first, the app's conversation and user ids hashed. Exactly what is sent is written first to `~/.cortad/<project>/history.json`. It talks to cortad.com and, when you choose one, to that tool's own address with the keys already in your env; those keys go nowhere else. It never connects to a database itself and never sends a connection string.
|
|
88
|
+
Your app gets this terminal's environment and its own request limits raised for the session, so simulated users are not turned away by them. Test conversations write where your app writes, after your yes in the browser.
|
|
100
89
|
|
|
101
90
|
## Files it creates
|
|
102
91
|
|
|
103
|
-
- `~/.cortad/<project>/` the key, the start command, the last tree digest, which process holds your app up, your app's output in `app.log`, the sign-in your own request carried, the run it asked for last, in `news.txt` the line a finished run left for your agent, removed once the agent has it, in `heard.txt` the ids of the runs your agent has read,
|
|
92
|
+
- `~/.cortad/<project>/` the key, the start command, the last tree digest, which process holds your app up, your app's output in `app.log`, the sign-in your own request carried, the run it asked for last, in `news.txt` the line a finished run left for your agent, removed once the agent has it, in `heard.txt` the ids of the runs your agent has read, and in `pairing.json` a link waiting for your approval
|
|
104
93
|
- your app's env file (`.env.local`, `.env` or `.dev.vars`, whichever it loads) Cortad's lines alone: `CORTAD_INGEST_KEY`, `CORTAD_PUBLIC_KEY` and `CORTAD_ENV=development`; only when git ignores the file
|
|
105
94
|
- `$TMPDIR/cortad-<pid>/` removed on exit
|
|
106
95
|
|
|
107
|
-
macOS and Linux, Node 20 or later. One dependency, playwright-core, loaded only to capture a request your app's own page makes.
|
|
96
|
+
macOS and Linux, Node 20 or later. One dependency, playwright-core, loaded only to capture a request your app's own page makes.
|
|
108
97
|
|
|
109
98
|
## Licence
|
|
110
99
|
|
package/lib/history/send.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
// What was selected, sent to Cortad in sends under 1 MiB each
|
|
2
|
-
// first. A send Cortad already has is taken once: running this again sends only what is missing.
|
|
1
|
+
// What was selected, sent to Cortad in sends under 1 MiB each. A send Cortad already has is taken once: running this again sends only what is missing.
|
|
3
2
|
const MAX_BYTES = 900_000;
|
|
4
3
|
const MAX_CONVERSATIONS = 500;
|
|
5
4
|
const TRIES = 5;
|
|
@@ -8,10 +8,7 @@ export const lines = (...parts) => parts.flat().filter((part) => part !== null &
|
|
|
8
8
|
const num = (n) => Number(n).toLocaleString("en-US");
|
|
9
9
|
const day = (iso) => (iso ? new Date(iso).toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric", timeZone: "UTC" }) : "");
|
|
10
10
|
|
|
11
|
-
export const header = (app) => [
|
|
12
|
-
`Cortad Intelligence for ${app}`,
|
|
13
|
-
"This command talks to cortad.com and, when you choose one, the tool your conversations are kept in. It writes Cortad's lines in your env file and files under ~/.cortad on this computer. It never edits your code or your coding agent's settings.",
|
|
14
|
-
];
|
|
11
|
+
export const header = (app) => [`Cortad Intelligence for ${app}`];
|
|
15
12
|
|
|
16
13
|
export const approveFirst = ({ spec, link, words, machine }) => lines(
|
|
17
14
|
"This computer needs the person's approval to connect to Cortad.",
|
|
@@ -26,7 +23,7 @@ export const keyRefused = "Cortad no longer accepts the key this computer kept f
|
|
|
26
23
|
|
|
27
24
|
export const keysWritten = (file, at) => [
|
|
28
25
|
`Wrote CORTAD_INGEST_KEY and CORTAD_PUBLIC_KEY to ${file}, lines ${at.ingest} and ${at.page}${at.env ? `, and CORTAD_ENV=development on line ${at.env} so chats on this computer are never counted as your users'` : ""}. ${file} is git-ignored.`,
|
|
29
|
-
`CORTAD_INGEST_KEY is a secret, so this names its line instead of printing it; open ${file} to see it
|
|
26
|
+
`CORTAD_INGEST_KEY is a secret, so this names its line instead of printing it; open ${file} to see it.`,
|
|
30
27
|
];
|
|
31
28
|
export const keysKept = (file) => [`Cortad's keys are already in ${file} and still work; they stay as they are.`];
|
|
32
29
|
export const keysManaged = (manager, path) => [
|
|
@@ -56,7 +53,6 @@ export function historyQuestion({ spec, sources, stores }) {
|
|
|
56
53
|
`Ask the person, with your question tool: "Send our users' newest 2,500 messages from the last 30 days to Cortad, so it can show us our users?" Cortad counts your app's replies and takes each conversation whole.`,
|
|
57
54
|
...options.map((o, i) => ` ${i + 1}. ${o}`),
|
|
58
55
|
...sources.map((s) => `${s.name} is read from this computer at ${s.host} with the keys in your env; the keys go to ${s.name} only.`),
|
|
59
|
-
"Before anything is sent, keys, card numbers, emails and phone numbers in the messages are replaced on this computer, and the app's conversation and user ids are hashed. What is sent goes to cortad.com, and a copy of exactly what was sent stays in ~/.cortad on this computer.",
|
|
60
56
|
"",
|
|
61
57
|
...exportContract(spec),
|
|
62
58
|
];
|
|
@@ -71,22 +67,19 @@ export const exportContract = (spec) => [
|
|
|
71
67
|
`Times need a zone (Z or +03:00) or epoch milliseconds. Tell the person which database host it reads. Pipe it to npx -y ${spec} intelligence history --stdin, then delete the script.`,
|
|
72
68
|
];
|
|
73
69
|
|
|
74
|
-
export function historySent({ result, picked, span, source
|
|
70
|
+
export function historySent({ result, picked, span, source }) {
|
|
75
71
|
const shortened = (why) => /kept to|cut at/.test(why);
|
|
76
72
|
const left = Object.entries(picked.left).filter(([why]) => !shortened(why)).map(([why, n]) => `${num(n)} ${why}`);
|
|
77
73
|
const cut = Object.entries(picked.left).filter(([why]) => shortened(why)).map(([why, n]) => `${num(n)} ${why}`);
|
|
78
74
|
return [
|
|
79
75
|
`Sent ${num(result.taken)} conversations from ${source}, ${day(span.from)} to ${day(span.to)}: ${num(picked.total)} messages, ${num(result.messages)} of them your app's replies, which is what Cortad counts.${result.already ? ` ${num(result.already)} were already in Cortad.` : ""}`,
|
|
80
|
-
...(picked.scrubbed ? [`${num(picked.scrubbed)} keys, card numbers, emails or phone numbers were replaced on this computer before sending.`] : []),
|
|
81
76
|
...(left.length ? [`Left out: ${left.join(", ")}.`] : []),
|
|
82
77
|
...(cut.length ? [`Shortened: ${cut.join(", ")}.`] : []),
|
|
83
|
-
`Exactly what was sent is in ${file}.`,
|
|
84
78
|
"Cortad is reading them now. They count against nothing: the plan's messages are for your live app.",
|
|
85
79
|
];
|
|
86
80
|
}
|
|
87
|
-
export const historyDry = ({ picked, span
|
|
81
|
+
export const historyDry = ({ picked, span }) => [
|
|
88
82
|
`Read ${num(picked.conversations.length)} conversations, ${day(span.from)} to ${day(span.to)}: ${num(picked.total)} messages, ${num(picked.messages)} of them your app's replies. Nothing was sent.`,
|
|
89
|
-
`What would be sent is in ${file}.`,
|
|
90
83
|
];
|
|
91
84
|
export const historyLooksLikeDevelopment = ({ why, spec, rest }) => [
|
|
92
85
|
`Nothing was sent: this looks like a development copy, because ${why}.`,
|
package/lib/intelligence.mjs
CHANGED
|
@@ -163,16 +163,13 @@ export async function intelligence(args, ctx) {
|
|
|
163
163
|
const picked = selected(raws, { key, cap: opts.cap, secrets: secretValues(ENV_FILES.map((f) => join(root, f)).filter(existsSync)).filter(credentialShaped) });
|
|
164
164
|
if (picked.conversations.length === 0) return { text: say.lines(say.historyNothing(label)), code: 1 };
|
|
165
165
|
const sends = packed(source, picked.conversations);
|
|
166
|
-
const file = join(homeOf(ctx.project), "history.json");
|
|
167
|
-
mkdirSync(homeOf(ctx.project), { recursive: true, mode: 0o700 });
|
|
168
|
-
writeFileSync(file, `${JSON.stringify(sends, null, 1)}\n`, { mode: 0o600 });
|
|
169
166
|
const span = spanOf(picked.conversations);
|
|
170
167
|
const why = looksLikeDevelopment({ host, conversations: picked.conversations, users: picked.users });
|
|
171
168
|
if (why && !opts.anyway) return { text: say.lines(say.historyLooksLikeDevelopment({ why, spec, rest: opts.from ? `--from ${opts.from}` : "--stdin" })) };
|
|
172
|
-
if (opts.dry) return { text: say.lines(say.historyDry({ picked, span
|
|
169
|
+
if (opts.dry) return { text: say.lines(say.historyDry({ picked, span })) };
|
|
173
170
|
const result = await sendAll(sends, { api: ctx.apiBase, token: token() });
|
|
174
171
|
if (result.refused) return { text: say.lines(`Cortad did not take the rest: ${result.refused}`, `${result.taken} conversations were taken before that.`), code: 1 };
|
|
175
|
-
return { text: say.lines(say.historySent({ result, picked, span, source: label
|
|
172
|
+
return { text: say.lines(say.historySent({ result, picked, span, source: label }), say.next(spec)), sent: true };
|
|
176
173
|
}
|
|
177
174
|
|
|
178
175
|
// The ingest key as this folder holds it: in its env file, or in keys.env when a manager holds its settings.
|
package/lib/read-text.mjs
CHANGED
|
@@ -83,29 +83,22 @@ export function readLines(r) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// Where each coding agent keeps the entry the connect command writes for Cortad, in the home folder.
|
|
86
|
-
const listed = (names) => (names.length > 1 ? `${names.slice(0, -1).join(", ")} and ${names.at(-1)}` : names[0]);
|
|
87
86
|
|
|
88
87
|
// What the connect command does on this machine, from the facts it keeps in the runner's record:
|
|
89
|
-
// what it started and stops,
|
|
90
|
-
//
|
|
91
|
-
// one the terminal says at connect (connectedText); the rest is read when someone asks.
|
|
88
|
+
// what it started and stops, what it sees, and an outside service a run would really reach. The first
|
|
89
|
+
// line is the one the terminal says at connect (connectedText); the rest is read when someone asks.
|
|
92
90
|
export function machineText(f) {
|
|
93
91
|
if (!f) return ["No connect command is running for this repository now, so nothing Cortad started is running here."];
|
|
94
92
|
const lines = [
|
|
95
93
|
f.started
|
|
96
|
-
? `Cortad started your app in ${f.dir === "." ? "this folder" : f.dir} and stops it when this command stops
|
|
97
|
-
: `Cortad uses your app already running on port ${f.port} and leaves it running
|
|
94
|
+
? `Cortad started your app in ${f.dir === "." ? "this folder" : f.dir} and stops it when this command stops.`
|
|
95
|
+
: `Cortad uses your app already running on port ${f.port} and leaves it running.`,
|
|
98
96
|
];
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
lines.push(f.hooked ? "Cortad sees each request that reaches your model and the model calls it makes. Request headers stay on this machine; the one sign-in Cortad's test requests use is kept in ~/.cortad/<project>/sign-in.json, readable by you alone, until your app refuses it or you delete it."
|
|
102
|
-
: f.proxy ? `A local proxy on port ${f.proxy} sees each request sent to it and the model calls your app makes through it. Request headers stay on this machine; the one sign-in Cortad's test requests use is kept in ~/.cortad/<project>/sign-in.json, readable by you alone, until your app refuses it or you delete it.`
|
|
97
|
+
lines.push(f.hooked ? "Cortad sees each request that reaches your model and the model calls it makes."
|
|
98
|
+
: f.proxy ? `A local proxy on port ${f.proxy} sees each request sent to it and the model calls your app makes through it.`
|
|
103
99
|
: "Cortad did not start your app, so it does not see the calls your app makes to its model.");
|
|
104
|
-
// Only the hook
|
|
105
|
-
|
|
106
|
-
: "During a run, what your app sends to an outside service, such as a payment or an email, is sent for real.");
|
|
107
|
-
lines.push(f.hooked ? "Files your app writes in its own folder during a run are put back when the run ends. Its log files are left as they are." : "Files your app writes in its own folder stay as it writes them.");
|
|
108
|
-
lines.push("Kept in ~/.cortad on this machine between connects: this repository's key, the key the test accounts' passwords are made from, what to put back after a run, and your app's output from the last session with your env values masked.");
|
|
100
|
+
// Only the hook answers an outside write itself: through the proxy, or with nothing loaded, a payment or an email goes out.
|
|
101
|
+
if (!f.hooked) lines.push("During a run, what your app sends to an outside service, such as a payment or an email, is sent for real.");
|
|
109
102
|
return lines;
|
|
110
103
|
}
|
|
111
104
|
// What the terminal says once the app answers: the one line the person needs.
|
package/local.mjs
CHANGED
|
@@ -54,7 +54,6 @@ import { report as reportLive } from "./lib/setup-live.mjs";
|
|
|
54
54
|
import { grpcCall } from "./lib/grpc.mjs";
|
|
55
55
|
|
|
56
56
|
// A count with its noun agreeing: 1 prompt, 3 prompts.
|
|
57
|
-
const counted = (n, one) => `${n} ${n === 1 ? one : `${one}s`}`;
|
|
58
57
|
|
|
59
58
|
const argv = process.argv.slice(2);
|
|
60
59
|
// The two faces a coding agent uses after the first connect (lib/cli.mjs): the MCP server the
|
|
@@ -678,10 +677,10 @@ async function startApp() {
|
|
|
678
677
|
return { port: wantedPort, cmd: null };
|
|
679
678
|
}
|
|
680
679
|
const sourceFiles = files.map((f) => join(root, f));
|
|
681
|
-
// Only its own request limits
|
|
680
|
+
// Only its own request limits: who the app lets in is never touched.
|
|
682
681
|
const lifted = liftedLimits(sourceFiles);
|
|
683
682
|
const raised = Object.keys(lifted);
|
|
684
|
-
if (raised.length) say(`
|
|
683
|
+
if (raised.length) say(`your app's own request limits are raised for this session only, so the simulated users are not turned away by them: ${raised.join(", ")}`);
|
|
685
684
|
launched = { cmd: typed, lifted };
|
|
686
685
|
if (proxyAsked || !hookable(appDir, typed)) await useProxy();
|
|
687
686
|
step(`starting your app with: ${shownCommand(typed, mask)}${fromSaved ? " (saved from your last connect)" : ""}`);
|
|
@@ -980,36 +979,12 @@ secrets = secretValues(envFiles, shellSecrets);
|
|
|
980
979
|
envKeys = [...secretEnvValues(envFiles, (f) => readFileSync(f, "utf8")), ...shellSecrets];
|
|
981
980
|
const kept = [];
|
|
982
981
|
for (let i = files.length - 1; i >= 0; i -= 1) if (carriesKey(files[i])) kept.unshift(...files.splice(i, 1));
|
|
983
|
-
// --explain:
|
|
984
|
-
//
|
|
982
|
+
// --explain: the files this folder would give, one line, for the check that keys and ignored files
|
|
983
|
+
// stay out (lib/sent-files.test.mjs). No network, no app started, nothing written.
|
|
985
984
|
if (explain) {
|
|
986
|
-
|
|
987
|
-
const left = listed ? [...listed].filter((f) => !going.has(f) && !ENV_FILE.test(basename(f))).sort() : [];
|
|
988
|
-
const bytes = files.reduce((n, f) => { try { return n + statSync(join(root, f)).size; } catch { return n; } }, 0);
|
|
989
|
-
const rel = (f) => relative(root, f) || ".";
|
|
990
|
-
console.log([
|
|
991
|
-
`cortad --explain (nothing is sent or started by this)`,
|
|
992
|
-
``,
|
|
993
|
-
`talks to ${origin.origin}, and localhost on your app's port only`,
|
|
994
|
-
`would send ${files.length} source files, ${Math.round(bytes / 1024)} KB, once${listed ? " (what git would commit)" : ""}`,
|
|
995
|
-
...(kept.length ? [`kept here ${kept.length} file${kept.length === 1 ? " that holds" : "s that hold"} keys: ${kept.slice(0, 6).join(", ")}${kept.length > 6 ? ", ..." : ""}`] : []),
|
|
996
|
-
...(left.length ? [`also not sent ${left.length} tracked media or lock file${left.length === 1 ? "" : "s"}: ${left.slice(0, 6).join(", ")}${left.length > 6 ? ", ..." : ""}`] : []),
|
|
997
|
-
`not sent anything git ignores, env files (${envFiles.length} here: ${envFiles.slice(0, 6).map(rel).join(", ") || "none"}), key files, node_modules`,
|
|
998
|
-
`env files read here only, to hide their values in what leaves and to know where your app's model settings point; nothing in them is handed to your app or sent`,
|
|
999
|
-
`would start ${wantedPort ? `nothing: uses your app on port ${wantedPort}, replies seen and model calls not` : typed ? `${typed} (your command, as typed, in ${rel(appDir)})` : "nothing: run again with your start command after --"}`,
|
|
1000
|
-
`would raise ${wantedPort || !typed ? "nothing" : `${Object.keys(liftedLimits(files.map((f) => join(root, f)))).join(", ") || "no request limits found"} (your app's own limits, for this session only; who it lets in is never touched)`}`,
|
|
1001
|
-
`loads into app lib/trace.cjs (Node, Bun) or lib/pyhook/sitecustomize.py (Python): records each request during which your app calls a model, what it answered and the model calls on the way; header values stay here`,
|
|
1002
|
-
`other apps a local proxy on 127.0.0.1 in front of your app's port and where its model settings point (OPENAI_BASE_URL and the others), recording the same; it passes each call on to the provider your settings name`,
|
|
1003
|
-
`your files never written by this program; your own coding agent edits them`,
|
|
1004
|
-
`your data nothing is copied, started or pointed elsewhere: your app uses the databases and services your command gives it, and test conversations write where it writes, after your yes on the card`,
|
|
1005
|
-
`test shell confined by the OS: your project and toolchains only, writes to temp and build folders, localhost only`,
|
|
1006
|
-
`for your agent an MCP entry and a skill in each coding agent's own home folder (Claude Code, Codex, Cursor), and a key in ~/.cortad for later runs`,
|
|
1007
|
-
``,
|
|
1008
|
-
`first files ${files.slice(0, 8).join(", ")}${files.length > 8 ? ", ..." : ""}`,
|
|
1009
|
-
].join("\n"));
|
|
985
|
+
console.log(`first files ${files.slice(0, 8).join(", ")}${files.length > 8 ? ", ..." : ""}`);
|
|
1010
986
|
process.exit(0);
|
|
1011
987
|
}
|
|
1012
|
-
if (kept.length) say(`left on this machine because it holds a key: ${kept.join(", ")}`);
|
|
1013
988
|
const keepSecret = (v) => { if (v && v.length >= 12 && !secrets.includes(v)) secrets.push(v); };
|
|
1014
989
|
// A real request that reached their model proves its door (lib/proof.mjs). What it proved goes up,
|
|
1015
990
|
// masked like everything else, and nothing goes up that the mask could not read; the sign-in each
|
|
@@ -1045,22 +1020,16 @@ key = attach.data.key;
|
|
|
1045
1020
|
// What this folder sends. A folder that sent its whole tree before goes on doing so: its read, its code
|
|
1046
1021
|
// versions and its history stay as they are. A new one sends its AI layer: the prompts, the evals, the
|
|
1047
1022
|
// code that calls the model, the routes that reach it and the shapes they read, chosen by the read's
|
|
1048
|
-
// own parsers (lib/layer.mjs)
|
|
1049
|
-
//
|
|
1023
|
+
// own parsers (lib/layer.mjs). The rest of the folder is never sent, and a read may not ask for it
|
|
1024
|
+
// later (readable). None of this is narrated: the person agreed to it before connecting, and a count
|
|
1025
|
+
// of files sent read to them as alarm.
|
|
1050
1026
|
// Only on the server's word: a server from before this answer sends none, and that is the tree.
|
|
1051
1027
|
if (attach.data.upload === "layer") {
|
|
1052
|
-
step("choosing what to send: the prompts, the evals and the code that calls the model");
|
|
1053
1028
|
const candidates = [];
|
|
1054
1029
|
for (const rel of files) { let full; try { full = readFileSync(join(root, rel), "utf8"); } catch { continue; } candidates.push({ path: rel, head: full.slice(0, 4000), size: Buffer.byteLength(full), full }); }
|
|
1055
1030
|
const chosen = layerOf(candidates);
|
|
1056
1031
|
const said = layerSaid(chosen);
|
|
1057
1032
|
layer = { ...said, set: new Set(chosen.files) };
|
|
1058
|
-
const sentList = join(homeOf(project), "sent.json");
|
|
1059
|
-
try { mkdirSync(homeOf(project), { recursive: true, mode: 0o700 }); writeFileSync(sentList, JSON.stringify({ ...said, files: chosen.files.map((f) => ({ path: f, why: chosen.why.get(f) })), leftOut: chosen.dropped ?? [] }, null, 1), { mode: 0o600 }); } catch { /* the list is for reading; the send does not depend on it */ }
|
|
1060
|
-
const left = said.leftOut.files ? ` ${said.leftOut.files} large data and test files were left out to keep the upload under 16 MB; they are named in the list.` : "";
|
|
1061
|
-
say(`sending the part of this code that is its AI, so simulated users can be written for it: ${counted(said.prompts, 'prompt')}, ${counted(said.modelCalls, 'model call')}, ${counted(said.evals, 'eval file')}, the routes that reach the model and the shapes they read. ${layer.set.size} of ${candidates.length} files; the list is at ${sentList}.${left} The rest of this folder is not sent.`);
|
|
1062
|
-
} else {
|
|
1063
|
-
say(`sending this folder's code as this project has before: ${files.length} files, env files and keys left out.`);
|
|
1064
1033
|
}
|
|
1065
1034
|
// What goes up. `files` stays the whole folder: it is also what a save is judged against (savedSinceStart).
|
|
1066
1035
|
const sending = layer ? files.filter((rel) => layer.set.has(rel)) : files;
|
|
@@ -1223,7 +1192,7 @@ async function appLife(firstStart) {
|
|
|
1223
1192
|
} : null);
|
|
1224
1193
|
become("up", { ...upAt(), app: launched ? child?.pid ?? null : null });
|
|
1225
1194
|
say(`your app is answering on port ${app.port}${app.cmd ? `, started with: ${shownCommand(app.cmd, mask)}` : ""}`);
|
|
1226
|
-
if (launched) say(`your app's output
|
|
1195
|
+
if (launched) say(`your app's output is in ${bootLog}`);
|
|
1227
1196
|
if (proxy) say(`send requests to port ${proxy.port}: it passes them to your app and Cortad sees each one. Requests sent straight to port ${app.port} are not seen`);
|
|
1228
1197
|
for (const line of connectedText(machineOf())) say(line);
|
|
1229
1198
|
say(CONNECTED);
|
package/package.json
CHANGED