@cortad/connector 0.6.6 → 0.6.8
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/lib/origins.mjs +10 -6
- package/local.mjs +5 -3
- package/package.json +1 -1
- package/skill/SKILL.md +1 -1
package/lib/origins.mjs
CHANGED
|
@@ -16,6 +16,9 @@ const NEAR = /origin|cors|allow|client|frontend|web_?url|site_?url|app_?url|publ
|
|
|
16
16
|
const ENV_ORIGIN = /ORIGIN|CORS|FRONTEND|CLIENT_URL|DOMAIN_CLIENT|WEB_URL|SITE_URL|APP_URL|PUBLIC_URL/;
|
|
17
17
|
const ENV_KEY = /^(?:X_)?API_KEY$|^APP_API_KEY$|^INTERNAL_API_KEY$|^(?:X_)?ADMIN_KEY$|^API_TOKEN$|^AUTH_TOKEN$|^SERVICE_KEY$|^SERVER_KEY$|^ACCESS_CODE$/;
|
|
18
18
|
const ENV_FILES = [".env", ".env.local", ".env.development", ".env.development.local"];
|
|
19
|
+
// The port the server's own code listens on when nothing overrides it (`PORT ?? '3100'`,
|
|
20
|
+
// `.listen(3100`): its own address is never its client, even when this session moved it elsewhere.
|
|
21
|
+
const OWN_PORT = /\bPORT\b['"\]]*\s*(?:\?\?|\|\|)\s*['"]?(\d{2,5})|\.listen\(\s*(\d{2,5})\b/g;
|
|
19
22
|
const FILE_CAP = 512_000;
|
|
20
23
|
|
|
21
24
|
export const envOf = (root) => {
|
|
@@ -33,8 +36,7 @@ export function clientOriginOf(root, { port = null, limit = 4000 } = {}) {
|
|
|
33
36
|
const scores = new Map();
|
|
34
37
|
const count = (url, by) => scores.set(url, (scores.get(url) ?? 0) + by);
|
|
35
38
|
let seen = 0;
|
|
36
|
-
const own = port ? Number(port) :
|
|
37
|
-
const portOf = (m) => Number(m[1] ?? (m[0].startsWith("https") ? 443 : 80));
|
|
39
|
+
const own = new Set(port ? [Number(port)] : []);
|
|
38
40
|
const walk = (dir, depth) => {
|
|
39
41
|
if (depth > 6 || seen > limit) return;
|
|
40
42
|
let entries;
|
|
@@ -46,19 +48,21 @@ export function clientOriginOf(root, { port = null, limit = 4000 } = {}) {
|
|
|
46
48
|
const file = join(dir, e.name);
|
|
47
49
|
let text;
|
|
48
50
|
try { if (statSync(file).size > FILE_CAP) continue; text = readFileSync(file, "utf8"); } catch { continue; }
|
|
51
|
+
for (const m of text.matchAll(OWN_PORT)) own.add(Number(m[1] ?? m[2]));
|
|
49
52
|
for (const m of text.matchAll(LOCAL)) {
|
|
50
|
-
if (own && portOf(m) === own) continue;
|
|
51
53
|
const around = text.slice(Math.max(0, m.index - 100), m.index);
|
|
52
54
|
count(m[0], NEAR.test(around) ? 3 : 1);
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
};
|
|
56
58
|
walk(root, 0);
|
|
57
|
-
|
|
59
|
+
const env = envOf(root);
|
|
60
|
+
if (/^\d{2,5}$/.test(String(env["PORT"] ?? ""))) own.add(Number(env["PORT"]));
|
|
61
|
+
for (const [name, value] of Object.entries(env)) {
|
|
58
62
|
if (!ENV_ORIGIN.test(name)) continue;
|
|
59
|
-
for (const m of String(value).matchAll(LOCAL))
|
|
63
|
+
for (const m of String(value).matchAll(LOCAL)) count(m[0], 4);
|
|
60
64
|
}
|
|
61
|
-
const best = [...scores].sort((a, b) => b[1] - a[1])[0];
|
|
65
|
+
const best = [...scores].filter(([url]) => !own.has(Number(new URL(url).port || (url.startsWith("https") ? 443 : 80)))).sort((a, b) => b[1] - a[1])[0];
|
|
62
66
|
return best ? best[0] : null;
|
|
63
67
|
}
|
|
64
68
|
|
package/local.mjs
CHANGED
|
@@ -1064,7 +1064,7 @@ const head = (() => {
|
|
|
1064
1064
|
const firstStart = startApp();
|
|
1065
1065
|
firstStart.catch(() => { /* appLife reads the failure */ });
|
|
1066
1066
|
const archive = join(work, "tree.tgz");
|
|
1067
|
-
step("connecting");
|
|
1067
|
+
step("connecting to your Cortad account");
|
|
1068
1068
|
// COPYFILE_DISABLE: a Mac's tar adds no "._name" entries.
|
|
1069
1069
|
await exec("tar", ["-czf", archive, "-C", root, "-T", list], { env: { ...process.env, COPYFILE_DISABLE: "1" } });
|
|
1070
1070
|
const bytes = readFileSync(archive);
|
|
@@ -1078,7 +1078,7 @@ const parts = Math.max(1, Math.ceil(bytes.length / PART));
|
|
|
1078
1078
|
async function sendTree() {
|
|
1079
1079
|
for (let off = 0; off < bytes.length; off += PART) {
|
|
1080
1080
|
const last = off + PART >= bytes.length;
|
|
1081
|
-
step(parts > 1 ? `connecting, ${Math.floor(off / PART) + 1} of ${parts}` : "connecting");
|
|
1081
|
+
step(parts > 1 ? `connecting to your Cortad account, ${Math.floor(off / PART) + 1} of ${parts}` : "connecting to your Cortad account");
|
|
1082
1082
|
// The last part asks for this machine's key when none is stored yet: minted once the repository
|
|
1083
1083
|
// row exists, kept in ~/.cortad for the runs a coding agent asks for on later days. A connect by a
|
|
1084
1084
|
// code asks too, since the code may be another account's: the key kept from the account before
|
|
@@ -1121,7 +1121,9 @@ async function sendHistory() {
|
|
|
1121
1121
|
}
|
|
1122
1122
|
}
|
|
1123
1123
|
// Unchanged code has already been read: coming back says so instead of claiming a second read.
|
|
1124
|
-
|
|
1124
|
+
// Said of the account, never of the app: a bare "connected" here, above an app that could not take its
|
|
1125
|
+
// port, was read as the app being up (2026-11-12).
|
|
1126
|
+
stepDone(moved ? "connected to your Cortad account: your change is in, and what Cortad read before still stands" : resumed ? "connected to your Cortad account: your code has not changed since last time" : "connected to your Cortad account");
|
|
1125
1127
|
if (head) void sendHistory().catch((err) => { if (verbose) say(`commit history not sent: ${err?.message ?? err}`); });
|
|
1126
1128
|
// The coding agents on this machine learn about Cortad now, once: an MCP entry and a skill in each
|
|
1127
1129
|
// one's own home folder. A run started by an agent later comes back through lib/cli.mjs.
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -17,7 +17,7 @@ Seven tools:
|
|
|
17
17
|
- `connect`: connect the live app, the same steps as `npx -y {{cortad}} intelligence`.
|
|
18
18
|
- `tell`: a note to the Cortad team; that a problem's question reads this app wrong (`findingId` and `why`); what counts as a good ending; behavioral questions to propose; an address for incidents.
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
The tools load with the coding agent's next session after the first connect. Until then, and wherever they are missing, every command is `npx {{cortad}} <command>` in a shell with the same output, the normal way to work in that session and nothing to mention to the person, for example `npx {{cortad}} behavior --against previous` or `npx {{cortad}} run_status`. The person's own instructions come before this skill.
|
|
21
21
|
|
|
22
22
|
## Talking to the person
|
|
23
23
|
|