@tryglen/cli 0.32.2 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_utils/codex-app-server.js +3 -0
- package/dist/_utils/codex-app-server.js.map +1 -1
- package/dist/_utils/ingest-diagnosis.d.ts +24 -0
- package/dist/_utils/ingest-diagnosis.js +50 -0
- package/dist/_utils/ingest-diagnosis.js.map +1 -0
- package/dist/_utils/ingest-heartbeat.d.ts +9 -0
- package/dist/_utils/ingest-heartbeat.js +53 -0
- package/dist/_utils/ingest-heartbeat.js.map +1 -0
- package/dist/_utils/install-receipt.d.ts +3 -0
- package/dist/_utils/install-receipt.js +21 -1
- package/dist/_utils/install-receipt.js.map +1 -1
- package/dist/_utils/login-check.d.ts +2 -0
- package/dist/_utils/login-check.js +60 -0
- package/dist/_utils/login-check.js.map +1 -0
- package/dist/_utils/shell-profile.d.ts +3 -1
- package/dist/_utils/shell-profile.js +53 -32
- package/dist/_utils/shell-profile.js.map +1 -1
- package/dist/bin.js +5 -1
- package/dist/bin.js.map +1 -1
- package/dist/commands/doctor.d.ts +8 -1
- package/dist/commands/doctor.js +87 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/ingest.js +9 -0
- package/dist/commands/ingest.js.map +1 -1
- package/dist/commands/install.js +40 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/session-start.js +2 -0
- package/dist/commands/session-start.js.map +1 -1
- package/dist/commands/skill.js +8 -0
- package/dist/commands/skill.js.map +1 -1
- package/dist/commands/uninstall.js +5 -1
- package/dist/commands/uninstall.js.map +1 -1
- package/package.json +1 -1
|
@@ -44,6 +44,9 @@ const listCodexHooks = (cwd) => new Promise((resolve) => {
|
|
|
44
44
|
const timer = setTimeout(() => finish(err("codex app-server timed out")), TIMEOUT_MS);
|
|
45
45
|
child.on("error", () => finish(err("could not spawn codex app-server")));
|
|
46
46
|
child.on("close", () => finish(err("codex app-server exited before answering")));
|
|
47
|
+
// A child that dies instantly closes stdin before our deferred write —
|
|
48
|
+
// without a listener that EPIPE crashes the process ('close' resolves err).
|
|
49
|
+
child.stdin.on("error", () => { });
|
|
47
50
|
const state = { buf: "" };
|
|
48
51
|
child.stdout.on("data", (chunk) => {
|
|
49
52
|
state.buf += chunk.toString();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-app-server.js","sourceRoot":"","sources":["../../src/_utils/codex-app-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAe,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAe1C,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B,MAAM,cAAc,GAAG,CAAC,GAAY,EAA2B,EAAE;IAC/D,MAAM,MAAM,GAAI,GAAuC,CAAC,MAAM,CAAC;IAC/D,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACxD,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAA6B,EAAE,CAAC;QACxD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,SAAS;QACzC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAkC,EAAE,CAAC;YACxD,IACE,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;gBACzB,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;gBAC7B,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;gBAC5B,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ;gBACjC,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,EACjC,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC;oBACX,GAAG,EAAE,CAAC,CAAC,GAAG;oBACV,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,MAAM,EAAE,CAAC,CAAC,MAAM;oBAChB,WAAW,EAAE,CAAC,CAAC,WAAW;oBAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;iBAC3B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,GAAG,GAAG,CAAC,EAAU,EAAE,MAAc,EAAE,MAAe,EAAU,EAAE,CAClE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC;AAEhE,MAAM,cAAc,GAAG,CACrB,GAAW,EACgC,EAAE,CAC7C,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;IACtB,yEAAyE;IACzE,yEAAyE;IACzE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,EAAE,CAAC,YAAY,CAAC,EAAE;QAC/D,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;KAClC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,CAAC,CAAmC,EAAQ,EAAE;QAC3D,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,KAAK,CAAC,IAAI,EAAE,CAAC;QACb,OAAO,CAAC,CAAC,CAAC,CAAC;IACb,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC,EAC/C,UAAU,CACX,CAAC;IAEF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC,CAAC,CAAC;IACzE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CACrB,MAAM,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC,CACxD,CAAC;
|
|
1
|
+
{"version":3,"file":"codex-app-server.js","sourceRoot":"","sources":["../../src/_utils/codex-app-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Result } from "./result.js";
|
|
2
|
+
type IngestState = "ok" | "failing" | "never" | "fresh-install" | "idle";
|
|
3
|
+
declare const diagnoseIngest: (input: {
|
|
4
|
+
agent: string;
|
|
5
|
+
heartbeat: Record<string, {
|
|
6
|
+
lastRunAt: number;
|
|
7
|
+
lastOutcome?: string;
|
|
8
|
+
lastSuccessAt?: number;
|
|
9
|
+
}>;
|
|
10
|
+
installedAt: string | null;
|
|
11
|
+
now: number;
|
|
12
|
+
}) => {
|
|
13
|
+
state: IngestState;
|
|
14
|
+
lastSuccessAt: number | null;
|
|
15
|
+
};
|
|
16
|
+
declare const codexNeverDifferential: (deps: {
|
|
17
|
+
listHooks: () => Promise<Result<{
|
|
18
|
+
command: string;
|
|
19
|
+
source: string;
|
|
20
|
+
trustStatus: string;
|
|
21
|
+
}[], string>>;
|
|
22
|
+
probeShell: (shell: string, args: string[]) => string | null;
|
|
23
|
+
}) => Promise<string[]>;
|
|
24
|
+
export { codexNeverDifferential, diagnoseIngest };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { GLEN_OWNER_PREFIX } from "./codex-hooks-merge.js";
|
|
2
|
+
// Under an hour post-install, silence is still "warming up", not a fault.
|
|
3
|
+
const FRESH_INSTALL_MS = 60 * 60 * 1000;
|
|
4
|
+
const diagnoseIngest = (input) => {
|
|
5
|
+
const entry = input.heartbeat[input.agent];
|
|
6
|
+
if (entry) {
|
|
7
|
+
const lastSuccessAt = entry.lastSuccessAt ?? null;
|
|
8
|
+
// Ran but no outcome (logged out / glen off / no completed turn) = idle,
|
|
9
|
+
// not failing — the hook plumbing is proven.
|
|
10
|
+
if (entry.lastOutcome === undefined)
|
|
11
|
+
return { state: "idle", lastSuccessAt };
|
|
12
|
+
return entry.lastOutcome === "success"
|
|
13
|
+
? { state: "ok", lastSuccessAt }
|
|
14
|
+
: { state: "failing", lastSuccessAt };
|
|
15
|
+
}
|
|
16
|
+
const installedAtMs = input.installedAt
|
|
17
|
+
? Date.parse(input.installedAt)
|
|
18
|
+
: null;
|
|
19
|
+
const fresh = installedAtMs === null || input.now - installedAtMs < FRESH_INSTALL_MS;
|
|
20
|
+
return { state: fresh ? "fresh-install" : "never", lastSuccessAt: null };
|
|
21
|
+
};
|
|
22
|
+
const UNTRUSTED_REMEDY = "open your agent and trust the glen hooks (/hooks), then start a new session — untrusted hooks are silently skipped";
|
|
23
|
+
const PATH_REMEDY = "`glen` is not on your login shell's PATH — run `glen update` or add the npm global bin dir to PATH";
|
|
24
|
+
const TOPOLOGY_REMEDY = "no cause found on this machine — if you connect to this machine from a local agent (Codex --remote / SSH), hooks run HERE; if the agent process runs elsewhere, install glen there and run `glen doctor` on that machine as the connecting user";
|
|
25
|
+
const HOOK_QUERY_FAILED_REMEDY = "could not query codex hook trust (codex app-server failed) — run `glen install --agent codex` to re-register and re-trust the hooks";
|
|
26
|
+
// Narrows never-ingested-despite-registered-hooks to untrusted hook / PATH
|
|
27
|
+
// gap / topology mismatch. Deps injected so tests spawn nothing.
|
|
28
|
+
const codexNeverDifferential = async (deps) => {
|
|
29
|
+
const hooks = await deps.listHooks();
|
|
30
|
+
if (!hooks.ok)
|
|
31
|
+
return [HOOK_QUERY_FAILED_REMEDY];
|
|
32
|
+
const remedies = [];
|
|
33
|
+
const untrustedGlenHook = hooks.value.some((h) => h.source === "user" &&
|
|
34
|
+
h.command.trimStart().startsWith(GLEN_OWNER_PREFIX) &&
|
|
35
|
+
h.trustStatus !== "trusted");
|
|
36
|
+
if (untrustedGlenHook)
|
|
37
|
+
remedies.push(UNTRUSTED_REMEDY);
|
|
38
|
+
const nonLoginPath = deps.probeShell("sh", ["-c", "command -v glen"]);
|
|
39
|
+
const loginPath = deps.probeShell(process.env.SHELL ?? "sh", [
|
|
40
|
+
"-lc",
|
|
41
|
+
"command -v glen",
|
|
42
|
+
]);
|
|
43
|
+
if (!nonLoginPath || !loginPath)
|
|
44
|
+
remedies.push(PATH_REMEDY);
|
|
45
|
+
if (remedies.length === 0)
|
|
46
|
+
remedies.push(TOPOLOGY_REMEDY);
|
|
47
|
+
return remedies;
|
|
48
|
+
};
|
|
49
|
+
export { codexNeverDifferential, diagnoseIngest };
|
|
50
|
+
//# sourceMappingURL=ingest-diagnosis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-diagnosis.js","sourceRoot":"","sources":["../../src/_utils/ingest-diagnosis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAG3D,0EAA0E;AAC1E,MAAM,gBAAgB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAIxC,MAAM,cAAc,GAAG,CAAC,KAQvB,EAAwD,EAAE;IACzD,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,IAAI,CAAC;QAClD,yEAAyE;QACzE,6CAA6C;QAC7C,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;YACjC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC,WAAW,KAAK,SAAS;YACpC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE;YAChC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;IAC1C,CAAC;IAED,MAAM,aAAa,GAAG,KAAK,CAAC,WAAW;QACrC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;QAC/B,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,KAAK,GACT,aAAa,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,GAAG,aAAa,GAAG,gBAAgB,CAAC;IACzE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;AAC3E,CAAC,CAAC;AAEF,MAAM,gBAAgB,GACpB,oHAAoH,CAAC;AACvH,MAAM,WAAW,GACf,oGAAoG,CAAC;AACvG,MAAM,eAAe,GACnB,iPAAiP,CAAC;AACpP,MAAM,wBAAwB,GAC5B,qIAAqI,CAAC;AAExI,2EAA2E;AAC3E,iEAAiE;AACjE,MAAM,sBAAsB,GAAG,KAAK,EAAE,IAKrC,EAAqB,EAAE;IACtB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,MAAM,iBAAiB,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CACxC,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,MAAM,KAAK,MAAM;QACnB,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC;QACnD,CAAC,CAAC,WAAW,KAAK,SAAS,CAC9B,CAAC;IACF,IAAI,iBAAiB;QAAE,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAEvD,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,EAAE;QAC3D,KAAK;QACL,iBAAiB;KAClB,CAAC,CAAC;IACH,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS;QAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAE5D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAE1D,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Entry = {
|
|
2
|
+
lastRunAt: number;
|
|
3
|
+
lastOutcome?: string;
|
|
4
|
+
lastSuccessAt?: number;
|
|
5
|
+
};
|
|
6
|
+
declare const readHeartbeat: () => Promise<Record<string, Entry>>;
|
|
7
|
+
declare const recordHeartbeatRun: (agent: string) => Promise<void>;
|
|
8
|
+
declare const recordHeartbeatOutcome: (agent: string, outcome: "success" | "failure" | "auth" | "426") => Promise<void>;
|
|
9
|
+
export { readHeartbeat, recordHeartbeatRun, recordHeartbeatOutcome };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
const heartbeatPath = () => join(process.env.GLEN_STATE_PATH
|
|
5
|
+
? dirname(process.env.GLEN_STATE_PATH)
|
|
6
|
+
: join(homedir(), ".glen"), "ingest-heartbeat.json");
|
|
7
|
+
// An entry only counts as "a hook ran here" when it carries a real timestamp —
|
|
8
|
+
// a hand-edited or half-written file must not fake a heartbeat (the login-check
|
|
9
|
+
// and doctor gates both read "any entry exists" as proof the plumbing works).
|
|
10
|
+
const isEntry = (value) => !!value &&
|
|
11
|
+
typeof value === "object" &&
|
|
12
|
+
Number.isFinite(value.lastRunAt);
|
|
13
|
+
const readHeartbeat = async () => {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(await readFile(heartbeatPath(), "utf8"));
|
|
16
|
+
if (!parsed || typeof parsed !== "object")
|
|
17
|
+
return {};
|
|
18
|
+
return Object.fromEntries(Object.entries(parsed).filter((entry) => isEntry(entry[1])));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const write = async (map) => {
|
|
25
|
+
try {
|
|
26
|
+
const path = heartbeatPath();
|
|
27
|
+
await mkdir(dirname(path), { recursive: true });
|
|
28
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
29
|
+
await writeFile(tmp, JSON.stringify(map));
|
|
30
|
+
await rename(tmp, path);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* best-effort — a heartbeat write must never break a hook */
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const recordHeartbeatRun = async (agent) => {
|
|
37
|
+
const map = await readHeartbeat();
|
|
38
|
+
await write({ ...map, [agent]: { ...map[agent], lastRunAt: Date.now() } });
|
|
39
|
+
};
|
|
40
|
+
const recordHeartbeatOutcome = async (agent, outcome) => {
|
|
41
|
+
const map = await readHeartbeat();
|
|
42
|
+
const prior = map[agent] ?? { lastRunAt: Date.now() };
|
|
43
|
+
await write({
|
|
44
|
+
...map,
|
|
45
|
+
[agent]: {
|
|
46
|
+
...prior,
|
|
47
|
+
lastOutcome: outcome,
|
|
48
|
+
...(outcome === "success" ? { lastSuccessAt: Date.now() } : {}),
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
export { readHeartbeat, recordHeartbeatRun, recordHeartbeatOutcome };
|
|
53
|
+
//# sourceMappingURL=ingest-heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-heartbeat.js","sourceRoot":"","sources":["../../src/_utils/ingest-heartbeat.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,6 +24,7 @@ type InstallReceipt = {
|
|
|
24
24
|
readonly gitHooks: FileSnapshot[];
|
|
25
25
|
readonly statePaths: string[];
|
|
26
26
|
readonly pendingInstall?: PendingInstall;
|
|
27
|
+
readonly installedAt?: string;
|
|
27
28
|
};
|
|
28
29
|
declare const installReceipt: {
|
|
29
30
|
path: () => string;
|
|
@@ -34,6 +35,8 @@ declare const installReceipt: {
|
|
|
34
35
|
recordGitHook: (snapshot: FileSnapshot) => Promise<Result<void, unknown>>;
|
|
35
36
|
recordPendingInstall: (pendingInstall: PendingInstall) => Promise<Result<void, unknown>>;
|
|
36
37
|
clearPendingInstall: () => Promise<Result<void, unknown>>;
|
|
38
|
+
recordInstalledAt: (iso: string) => Promise<Result<void, unknown>>;
|
|
39
|
+
installedAt: () => Promise<string | null>;
|
|
37
40
|
recordStatePath: (path: string) => Promise<Result<void, unknown>>;
|
|
38
41
|
remove: () => Promise<Result<void, unknown>>;
|
|
39
42
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { chmod, mkdir, readFile, rename, unlink, writeFile, } from "node:fs/promises";
|
|
1
|
+
import { chmod, mkdir, readFile, rename, stat, unlink, writeFile, } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { fromPromise, ok } from "./result.js";
|
|
@@ -85,6 +85,9 @@ const parseReceipt = (raw) => {
|
|
|
85
85
|
typeof codex.configExisted !== "boolean"))
|
|
86
86
|
return null;
|
|
87
87
|
const pendingInstall = parsePendingInstall(value.pendingInstall);
|
|
88
|
+
const installedAt = isIsoTimestamp(value.installedAt)
|
|
89
|
+
? value.installedAt
|
|
90
|
+
: null;
|
|
88
91
|
return {
|
|
89
92
|
version: 1,
|
|
90
93
|
shellProfiles: value.shellProfiles,
|
|
@@ -93,6 +96,7 @@ const parseReceipt = (raw) => {
|
|
|
93
96
|
gitHooks: value.gitHooks,
|
|
94
97
|
statePaths: value.statePaths,
|
|
95
98
|
...(pendingInstall ? { pendingInstall } : {}),
|
|
99
|
+
...(installedAt ? { installedAt } : {}),
|
|
96
100
|
};
|
|
97
101
|
};
|
|
98
102
|
const read = async () => {
|
|
@@ -138,7 +142,21 @@ const recordGitHook = async (snapshot) => update((current) => {
|
|
|
138
142
|
return { ...current, gitHooks };
|
|
139
143
|
});
|
|
140
144
|
const recordPendingInstall = async (pendingInstall) => update((current) => ({ ...current, pendingInstall }));
|
|
145
|
+
// First install wins — a later `glen install` must not reset the age clock,
|
|
146
|
+
// or a machine that has never recorded a turn would look permanently fresh.
|
|
147
|
+
const recordInstalledAt = async (iso) => update((current) => current.installedAt ? current : { ...current, installedAt: iso });
|
|
141
148
|
const clearPendingInstall = async () => update(({ pendingInstall: _pendingInstall, ...current }) => current);
|
|
149
|
+
// Most durable source first: installedAt → pendingInstall (not yet POSTed) →
|
|
150
|
+
// receipt mtime (pre-installedAt receipts) → null (no receipt).
|
|
151
|
+
const installedAt = async () => {
|
|
152
|
+
const receipt = await read();
|
|
153
|
+
if (receipt?.installedAt)
|
|
154
|
+
return receipt.installedAt;
|
|
155
|
+
if (receipt?.pendingInstall)
|
|
156
|
+
return receipt.pendingInstall.occurredAt;
|
|
157
|
+
const stats = await fromPromise(stat(receiptPath()));
|
|
158
|
+
return stats.ok ? stats.value.mtime.toISOString() : null;
|
|
159
|
+
};
|
|
142
160
|
const remove = async () => {
|
|
143
161
|
const result = await fromPromise(unlink(receiptPath()));
|
|
144
162
|
if (!result.ok && result.error.code !== "ENOENT")
|
|
@@ -157,6 +175,8 @@ const installReceipt = {
|
|
|
157
175
|
recordGitHook,
|
|
158
176
|
recordPendingInstall,
|
|
159
177
|
clearPendingInstall,
|
|
178
|
+
recordInstalledAt,
|
|
179
|
+
installedAt,
|
|
160
180
|
recordStatePath: (path) => update((current) => ({
|
|
161
181
|
...current,
|
|
162
182
|
statePaths: [...new Set([...current.statePaths, path])],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-receipt.js","sourceRoot":"","sources":["../../src/_utils/install-receipt.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,MAAM,EACN,SAAS,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAe,WAAW,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"install-receipt.js","sourceRoot":"","sources":["../../src/_utils/install-receipt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { readCredentials } from "./credentials.js";
|
|
5
|
+
import { readHeartbeat } from "./ingest-heartbeat.js";
|
|
6
|
+
import { installReceipt } from "./install-receipt.js";
|
|
7
|
+
import { fromPromise } from "./result.js";
|
|
8
|
+
const WARNING_LINE = "glen: installed here, but no agent turns have ever been recorded from this machine — hooks fire where the agent process runs. Run `glen doctor` here for a diagnosis.";
|
|
9
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
10
|
+
// Same dir-derivation rule as the heartbeat file: co-located with GLEN_STATE_PATH.
|
|
11
|
+
const memoPath = () => join(process.env.GLEN_STATE_PATH
|
|
12
|
+
? dirname(process.env.GLEN_STATE_PATH)
|
|
13
|
+
: join(homedir(), ".glen"), "login-check-memo.json");
|
|
14
|
+
const readMemo = async () => {
|
|
15
|
+
const read = await fromPromise(readFile(memoPath(), "utf8"));
|
|
16
|
+
if (!read.ok)
|
|
17
|
+
return null;
|
|
18
|
+
const parsed = await fromPromise(Promise.resolve(JSON.parse(read.value)));
|
|
19
|
+
if (!parsed.ok)
|
|
20
|
+
return null;
|
|
21
|
+
const data = parsed.value;
|
|
22
|
+
return data &&
|
|
23
|
+
typeof data === "object" &&
|
|
24
|
+
typeof data.lastPrintedAt === "number"
|
|
25
|
+
? data
|
|
26
|
+
: null;
|
|
27
|
+
};
|
|
28
|
+
const writeMemo = async (lastPrintedAt) => {
|
|
29
|
+
try {
|
|
30
|
+
const path = memoPath();
|
|
31
|
+
await mkdir(dirname(path), { recursive: true });
|
|
32
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
33
|
+
await writeFile(tmp, JSON.stringify({ lastPrintedAt }));
|
|
34
|
+
await rename(tmp, path);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
/* best-effort — a memo write must never break the login-check hook */
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const runLoginCheck = async (now = Date.now()) => {
|
|
41
|
+
const creds = await readCredentials();
|
|
42
|
+
if (!creds)
|
|
43
|
+
return null;
|
|
44
|
+
const installedAt = await installReceipt.installedAt();
|
|
45
|
+
if (!installedAt)
|
|
46
|
+
return null;
|
|
47
|
+
const heartbeat = await readHeartbeat();
|
|
48
|
+
if (Object.keys(heartbeat).length > 0)
|
|
49
|
+
return null;
|
|
50
|
+
const installedAtMs = Date.parse(installedAt);
|
|
51
|
+
if (Number.isNaN(installedAtMs) || now - installedAtMs < DAY_MS)
|
|
52
|
+
return null;
|
|
53
|
+
const memo = await readMemo();
|
|
54
|
+
if (memo && now - memo.lastPrintedAt < DAY_MS)
|
|
55
|
+
return null;
|
|
56
|
+
await writeMemo(now);
|
|
57
|
+
return WARNING_LINE;
|
|
58
|
+
};
|
|
59
|
+
export { runLoginCheck };
|
|
60
|
+
//# sourceMappingURL=login-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-check.js","sourceRoot":"","sources":["../../src/_utils/login-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,4 +8,6 @@ declare const glenPathBlock: (binDir: string) => string;
|
|
|
8
8
|
declare const glenPathBlockPresent: (profileText: string) => boolean;
|
|
9
9
|
declare const addGlenPathBlock: (profilePath: string, binDir: string) => Promise<Result<"added" | "updated" | "already-present", CliError>>;
|
|
10
10
|
declare const removeGlenPathBlock: (profilePath: string) => Promise<Result<"removed" | "absent", CliError>>;
|
|
11
|
-
|
|
11
|
+
declare const addGlenLoginCheckBlock: (profilePath: string) => Promise<Result<"added" | "updated" | "already-present", CliError>>;
|
|
12
|
+
declare const removeGlenLoginCheckBlock: (profilePath: string) => Promise<Result<"removed" | "absent", CliError>>;
|
|
13
|
+
export { addGlenLoginCheckBlock, addGlenPathBlock, glenPathBlock, glenPathBlockPresent, loginProfileForShell, removeGlenLoginCheckBlock, removeGlenPathBlock, };
|
|
@@ -3,8 +3,13 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
4
|
import { causeOf, cliErr } from "./cli-error.js";
|
|
5
5
|
import { err, fromPromise, ok } from "./result.js";
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// "# glen" is a substring-prefix of "# glen login-check" — fencedSpan matches
|
|
7
|
+
// markers as whole lines so the two fences never cross-match.
|
|
8
|
+
const PATH_MARKERS = { start: "# glen", end: "# glen end" };
|
|
9
|
+
const LOGIN_CHECK_MARKERS = {
|
|
10
|
+
start: "# glen login-check",
|
|
11
|
+
end: "# glen login-check end",
|
|
12
|
+
};
|
|
8
13
|
// The login-shell profile for the current shell, or null for shells we don't
|
|
9
14
|
// auto-edit (fish/others fall back to a printed manual line). We target the
|
|
10
15
|
// LOGIN profile — `.zprofile` / `.bash_profile` — because the agents' hooks
|
|
@@ -31,28 +36,40 @@ const loginProfileForShell = () => {
|
|
|
31
36
|
// path (the npm prefix), so we don't escape `"`/`$`/`` ` `` — they don't occur
|
|
32
37
|
// in a real prefix and escaping them would only obscure the block.
|
|
33
38
|
const glenPathBlock = (binDir) => [
|
|
34
|
-
|
|
39
|
+
PATH_MARKERS.start,
|
|
35
40
|
'case ":$PATH:" in',
|
|
36
41
|
` *":${binDir}:"*) ;;`,
|
|
37
42
|
` *) export PATH="${binDir}:$PATH" ;;`,
|
|
38
43
|
"esac",
|
|
39
|
-
|
|
44
|
+
PATH_MARKERS.end,
|
|
40
45
|
].join("\n");
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
// `[ -t 0 ]` keeps the warning out of non-terminal login shells (scp/rsync/
|
|
47
|
+
// `ssh host <cmd>` and the agents' `$SHELL -lc` hook streams). `>&2 2>/dev/null`
|
|
48
|
+
// order is deliberate: stdout onto the ORIGINAL stderr first, then stderr off.
|
|
49
|
+
const LOGIN_CHECK_BODY = "if [ -t 0 ] && command -v glen >/dev/null 2>&1; then glen doctor --login-check >&2 2>/dev/null; fi";
|
|
50
|
+
const glenLoginCheckBlock = () => [LOGIN_CHECK_MARKERS.start, LOGIN_CHECK_BODY, LOGIN_CHECK_MARKERS.end].join("\n");
|
|
51
|
+
// Markers match as whole trimmed lines (see PATH_MARKERS note). Returns the
|
|
52
|
+
// fence's [start, end) character span, or null when absent/malformed.
|
|
53
|
+
const fencedSpan = (text, markers) => {
|
|
54
|
+
const lines = [];
|
|
55
|
+
let offset = 0;
|
|
56
|
+
for (const line of text.split("\n")) {
|
|
57
|
+
lines.push({ line, offset });
|
|
58
|
+
offset += line.length + 1;
|
|
59
|
+
}
|
|
60
|
+
const startIdx = lines.findIndex((l) => l.line.trim() === markers.start);
|
|
61
|
+
if (startIdx === -1)
|
|
62
|
+
return null;
|
|
63
|
+
const endIdx = lines.findIndex((l, i) => i > startIdx && l.line.trim() === markers.end);
|
|
64
|
+
if (endIdx === -1)
|
|
49
65
|
return null;
|
|
50
|
-
const
|
|
51
|
-
|
|
66
|
+
const startLine = lines[startIdx];
|
|
67
|
+
const endLine = lines[endIdx];
|
|
68
|
+
if (!startLine || !endLine)
|
|
52
69
|
return null;
|
|
53
|
-
return { start, end:
|
|
70
|
+
return { start: startLine.offset, end: endLine.offset + endLine.line.length };
|
|
54
71
|
};
|
|
55
|
-
const glenPathBlockPresent = (profileText) =>
|
|
72
|
+
const glenPathBlockPresent = (profileText) => fencedSpan(profileText, PATH_MARKERS) !== null;
|
|
56
73
|
const atomicWrite = async (filePath, content) => {
|
|
57
74
|
const mk = await fromPromise(mkdir(dirname(filePath), { recursive: true }));
|
|
58
75
|
if (!mk.ok)
|
|
@@ -76,16 +93,16 @@ const joinAround = (before, after) => {
|
|
|
76
93
|
return `${head}\n`;
|
|
77
94
|
return `${head}\n\n${tail}\n`;
|
|
78
95
|
};
|
|
79
|
-
// Ensure the login profile contains exactly the canonical
|
|
80
|
-
// creating the file if missing. No existing
|
|
81
|
-
// identical block already present → no write
|
|
82
|
-
// DIFFERS (hand-edited, or
|
|
83
|
-
// never leave a duplicate or stale fence behind
|
|
84
|
-
|
|
96
|
+
// Ensure the login profile contains exactly the canonical fenced `block`,
|
|
97
|
+
// creating the file if missing. No existing fence (by MARKERS) → append
|
|
98
|
+
// ("added"). An identical block already present → no write
|
|
99
|
+
// ("already-present"). A block that DIFFERS (hand-edited, or changed inputs)
|
|
100
|
+
// → replace it in place so we never leave a duplicate or stale fence behind
|
|
101
|
+
// ("updated"). Atomic write.
|
|
102
|
+
const addFencedBlock = async (profilePath, markers, block, remedy) => {
|
|
85
103
|
const existing = await fromPromise(readFile(profilePath, "utf-8"));
|
|
86
104
|
const current = existing.ok ? existing.value : "";
|
|
87
|
-
const
|
|
88
|
-
const span = glenBlockSpan(current);
|
|
105
|
+
const span = fencedSpan(current, markers);
|
|
89
106
|
if (span && current.slice(span.start, span.end) === block) {
|
|
90
107
|
return ok("already-present");
|
|
91
108
|
}
|
|
@@ -99,20 +116,20 @@ const addGlenPathBlock = async (profilePath, binDir) => {
|
|
|
99
116
|
what: "could not update your shell profile",
|
|
100
117
|
why: causeOf(w.error),
|
|
101
118
|
path: profilePath,
|
|
102
|
-
remedy
|
|
119
|
+
remedy,
|
|
103
120
|
}));
|
|
104
121
|
}
|
|
105
122
|
return ok(outcome);
|
|
106
123
|
};
|
|
107
|
-
// Strip
|
|
108
|
-
// every position (start / middle / end / only-content). Absent
|
|
109
|
-
// file → "absent" (no-op).
|
|
110
|
-
const
|
|
124
|
+
// Strip a marker-fenced block (by MARKERS) for `glen uninstall`, leaving clean
|
|
125
|
+
// output for every position (start / middle / end / only-content). Absent
|
|
126
|
+
// block / missing file → "absent" (no-op).
|
|
127
|
+
const removeFencedBlock = async (profilePath, markers, remedy) => {
|
|
111
128
|
const existing = await fromPromise(readFile(profilePath, "utf-8"));
|
|
112
129
|
if (!existing.ok)
|
|
113
130
|
return ok("absent");
|
|
114
131
|
const text = existing.value;
|
|
115
|
-
const span =
|
|
132
|
+
const span = fencedSpan(text, markers);
|
|
116
133
|
if (!span)
|
|
117
134
|
return ok("absent");
|
|
118
135
|
const next = joinAround(text.slice(0, span.start), text.slice(span.end));
|
|
@@ -122,10 +139,14 @@ const removeGlenPathBlock = async (profilePath) => {
|
|
|
122
139
|
what: "could not update your shell profile",
|
|
123
140
|
why: causeOf(w.error),
|
|
124
141
|
path: profilePath,
|
|
125
|
-
remedy
|
|
142
|
+
remedy,
|
|
126
143
|
}));
|
|
127
144
|
}
|
|
128
145
|
return ok("removed");
|
|
129
146
|
};
|
|
130
|
-
|
|
147
|
+
const addGlenPathBlock = (profilePath, binDir) => addFencedBlock(profilePath, PATH_MARKERS, glenPathBlock(binDir), `add this line manually, then restart your terminal: export PATH="${binDir}:$PATH"`);
|
|
148
|
+
const removeGlenPathBlock = (profilePath) => removeFencedBlock(profilePath, PATH_MARKERS, `remove the \`${PATH_MARKERS.start}\` … \`${PATH_MARKERS.end}\` block from ${profilePath} manually`);
|
|
149
|
+
const addGlenLoginCheckBlock = (profilePath) => addFencedBlock(profilePath, LOGIN_CHECK_MARKERS, glenLoginCheckBlock(), `add this line manually, then restart your terminal: ${LOGIN_CHECK_BODY}`);
|
|
150
|
+
const removeGlenLoginCheckBlock = (profilePath) => removeFencedBlock(profilePath, LOGIN_CHECK_MARKERS, `remove the \`${LOGIN_CHECK_MARKERS.start}\` … \`${LOGIN_CHECK_MARKERS.end}\` block from ${profilePath} manually`);
|
|
151
|
+
export { addGlenLoginCheckBlock, addGlenPathBlock, glenPathBlock, glenPathBlockPresent, loginProfileForShell, removeGlenLoginCheckBlock, removeGlenPathBlock, };
|
|
131
152
|
//# sourceMappingURL=shell-profile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-profile.js","sourceRoot":"","sources":["../../src/_utils/shell-profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAiB,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAe,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"shell-profile.js","sourceRoot":"","sources":["../../src/_utils/shell-profile.ts"],"names":[],"mappings":"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"}
|
package/dist/bin.js
CHANGED
|
@@ -482,9 +482,13 @@ cli
|
|
|
482
482
|
cli
|
|
483
483
|
.command("doctor", "Show version, update status, session state, and plugin hints")
|
|
484
484
|
.option("--auto", "Quiet mode: run background update + plugin upgrades (used by hooks)")
|
|
485
|
+
.option("--login-check", "Print a one-line warning when glen is installed but has never recorded from this machine (used by the shell-profile hook)")
|
|
485
486
|
.action(async (options) => {
|
|
486
487
|
const { doctor } = await import("./commands/doctor.js");
|
|
487
|
-
const result = await doctor(
|
|
488
|
+
const result = await doctor({
|
|
489
|
+
auto: options.auto === true,
|
|
490
|
+
loginCheck: options.loginCheck === true,
|
|
491
|
+
});
|
|
488
492
|
if (!result.ok) {
|
|
489
493
|
process.stderr.write(formatCliError(result.error));
|
|
490
494
|
process.exit(1);
|