@tryglen/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -0
- package/dist/_utils/agent-adapter.d.ts +19 -0
- package/dist/_utils/agent-adapter.js +50 -0
- package/dist/_utils/agent-adapter.js.map +1 -0
- package/dist/_utils/build-turn-body.d.ts +2 -1
- package/dist/_utils/build-turn-body.js +3 -0
- package/dist/_utils/build-turn-body.js.map +1 -1
- package/dist/_utils/config.d.ts +2 -2
- package/dist/_utils/config.js +2 -2
- package/dist/_utils/config.js.map +1 -1
- package/dist/_utils/git-context.d.ts +10 -0
- package/dist/_utils/git-context.js +32 -0
- package/dist/_utils/git-context.js.map +1 -0
- package/dist/_utils/glen-headers.d.ts +8 -0
- package/dist/_utils/glen-headers.js +8 -0
- package/dist/_utils/glen-headers.js.map +1 -0
- package/dist/_utils/http.d.ts +1 -0
- package/dist/_utils/http.js +1 -0
- package/dist/_utils/http.js.map +1 -1
- package/dist/_utils/idempotency-key.d.ts +1 -1
- package/dist/_utils/idempotency-key.js +6 -2
- package/dist/_utils/idempotency-key.js.map +1 -1
- package/dist/_utils/state-line.d.ts +3 -0
- package/dist/_utils/state-line.js +10 -0
- package/dist/_utils/state-line.js.map +1 -0
- package/dist/_utils/state.d.ts +13 -0
- package/dist/_utils/state.js +41 -0
- package/dist/_utils/state.js.map +1 -0
- package/dist/_utils/update.d.ts +11 -0
- package/dist/_utils/update.js +122 -0
- package/dist/_utils/update.js.map +1 -0
- package/dist/_utils/version.d.ts +2 -0
- package/dist/_utils/version.js +10 -0
- package/dist/_utils/version.js.map +1 -0
- package/dist/_utils/which.d.ts +6 -0
- package/dist/_utils/which.js +16 -0
- package/dist/_utils/which.js.map +1 -0
- package/dist/bin.js +101 -15
- package/dist/bin.js.map +1 -1
- package/dist/commands/doctor.d.ts +13 -0
- package/dist/commands/doctor.js +91 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/incognito.d.ts +3 -0
- package/dist/commands/incognito.js +19 -0
- package/dist/commands/incognito.js.map +1 -0
- package/dist/commands/ingest.d.ts +1 -1
- package/dist/commands/ingest.js +59 -9
- package/dist/commands/ingest.js.map +1 -1
- package/dist/commands/install.js +113 -26
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/login.js +26 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.js +2 -0
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/org.d.ts +4 -0
- package/dist/commands/org.js +80 -0
- package/dist/commands/org.js.map +1 -0
- package/dist/commands/search.js +29 -5
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/session-start.d.ts +19 -0
- package/dist/commands/session-start.js +57 -0
- package/dist/commands/session-start.js.map +1 -0
- package/dist/commands/status.js +24 -5
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/statusline.d.ts +13 -0
- package/dist/commands/statusline.js +38 -0
- package/dist/commands/statusline.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.js +35 -0
- package/dist/commands/update.js.map +1 -0
- package/package.json +1 -1
- package/dist/commands/private.d.ts +0 -6
- package/dist/commands/private.js +0 -40
- package/dist/commands/private.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# @tryglen/cli
|
|
2
|
+
|
|
3
|
+
Glen CLI — shared team memory for coding agents.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @tryglen/cli
|
|
9
|
+
glen login
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Commands
|
|
13
|
+
|
|
14
|
+
| Command | Description |
|
|
15
|
+
|---|---|
|
|
16
|
+
| `glen login` | Connect via browser sign-in; writes credentials and initial session state. |
|
|
17
|
+
| `glen logout` | Sign out and remove credentials + session state. |
|
|
18
|
+
| `glen status` | Show connection status, active org, incognito state, and CLI version. |
|
|
19
|
+
| `glen search "<query>"` | Search the team's shared glen memory. |
|
|
20
|
+
| `glen incognito on\|off` | Toggle incognito mode: while on, nothing is recorded (recall keeps working). |
|
|
21
|
+
| `glen org list` | List your organizations; active one is marked with `✓`. |
|
|
22
|
+
| `glen org switch [slug]` | Switch the active organization for this machine (interactive picker without `slug`). |
|
|
23
|
+
| `glen ingest [--agent <name>]` | Ingest a turn from stdin — used by agent hooks (`claude-code` or `codex`). |
|
|
24
|
+
| `glen install` | Detect coding agents and install glen plugins. |
|
|
25
|
+
| `glen update` | Update the glen CLI to the latest version. |
|
|
26
|
+
| `glen doctor [--auto]` | Show version, update status, and session state. `--auto` runs background updates silently (used by hooks). |
|
|
27
|
+
| `glen statusline` | Output a single status line for the agent's UI status bar — used by hooks. |
|
|
28
|
+
| `glen session-start [--agent <name>]` | Output hook JSON for the agent's `SessionStart` event — used by hooks. |
|
|
29
|
+
| `glen version` | Print the CLI version. |
|
|
30
|
+
|
|
31
|
+
## Files
|
|
32
|
+
|
|
33
|
+
| File | Description |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `~/.glen/credentials.json` | API key credential — mode `0600`, never logged or sent to anyone but `tryglen.com`. |
|
|
36
|
+
| `~/.glen/state.json` | Client-owned session state: `{ activeOrgId, activeOrgSlug, activeOrgName, incognito }`. Non-secret. |
|
|
37
|
+
|
|
38
|
+
The state file is what the CLI sends as `x-glen-org-id` / `x-glen-incognito` headers on every request. The server validates org membership on every call — no state is stored server-side.
|
|
39
|
+
|
|
40
|
+
## Environment variables
|
|
41
|
+
|
|
42
|
+
| Variable | Default | Description |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `GLEN_BASE_URL` | `https://app.tryglen.com` | Override the glen server URL (useful for local dev). |
|
|
45
|
+
| `GLEN_CREDENTIALS_PATH` | `~/.glen/credentials.json` | Override credentials file location. |
|
|
46
|
+
| `GLEN_STATE_PATH` | `~/.glen/state.json` | Override state file location. |
|
|
47
|
+
| `GLEN_NO_AUTO_UPDATE` | unset | Set to `1` to disable automatic background updates. Also disabled when `CI=true`. |
|
|
48
|
+
|
|
49
|
+
## Update behavior
|
|
50
|
+
|
|
51
|
+
The CLI uses layered silent auto-update so you stay current without thinking about it:
|
|
52
|
+
|
|
53
|
+
1. **Background TTL check (daily):** once per 24 hours, when installed via npm globally, the CLI spawns a detached `npm install -g @tryglen/cli@latest` process that doesn't block your turn. A `~/.glen/update-check.json` timestamp controls the cadence.
|
|
54
|
+
2. **SessionStart hook (`glen doctor --auto`):** on every agent session start, the hook also triggers background update + plugin upgrade hints. Same detached, non-blocking approach.
|
|
55
|
+
3. **Server-driven 426 floor:** if the server requires a newer CLI version (breaking API change), it responds with `HTTP 426 Upgrade Required`. The CLI then runs a synchronous self-update and re-execs your original command with `GLEN_REEXEC=1` set to prevent infinite loops. If the update fails or you are below the minimum after re-exec, it exits with a message to run `npm i -g @tryglen/cli@latest` manually.
|
|
56
|
+
|
|
57
|
+
Escape hatches:
|
|
58
|
+
- `GLEN_NO_AUTO_UPDATE=1` disables all background update attempts.
|
|
59
|
+
- `CI=true` (set automatically in GitHub Actions and most CI systems) also disables them.
|
|
60
|
+
- The CLI only self-updates when installed globally via npm (it detects this by comparing the binary's real path against `npm prefix -g`). If you installed via another package manager, you get a hint to update with your own tooling instead.
|
|
61
|
+
|
|
62
|
+
## Troubleshooting
|
|
63
|
+
|
|
64
|
+
**`glen status` shows "not connected"**
|
|
65
|
+
Run `glen login` to authenticate via browser.
|
|
66
|
+
|
|
67
|
+
**`glen search` / `glen ingest` prints "no active organization"**
|
|
68
|
+
Run `glen org switch` (or `glen login` if you've never connected) to pick an organization.
|
|
69
|
+
|
|
70
|
+
**426 error in CI or non-npm installs**
|
|
71
|
+
The server requires a newer CLI version. Run `npm i -g @tryglen/cli@latest` (or equivalent for your package manager) and retry.
|
|
72
|
+
|
|
73
|
+
**Stuck update lock**
|
|
74
|
+
If a previous update was interrupted, a stale lock can prevent future auto-updates:
|
|
75
|
+
```bash
|
|
76
|
+
rm -rf ~/.glen/update.lock
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**`glen doctor` for a full picture**
|
|
80
|
+
Run `glen doctor` to see your version, the latest available, install channel, and session state in one place.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type HookEvent = Record<string, unknown>;
|
|
2
|
+
type SessionFields = Record<string, string>;
|
|
3
|
+
type AdapterOutput = {
|
|
4
|
+
readonly sessionFields: SessionFields;
|
|
5
|
+
readonly priorAssistant: string | null;
|
|
6
|
+
readonly usesTranscript: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Normalise a raw hook event into session fields, priorAssistant context, and
|
|
10
|
+
* transcript usage flag — varying by agent type.
|
|
11
|
+
*
|
|
12
|
+
* - "codex": reads model/permission_mode/source + last_assistant_message
|
|
13
|
+
* directly from the event (no transcript file).
|
|
14
|
+
* - "claude-code" and generic/unknown: uses parseTranscript for priorAssistant
|
|
15
|
+
* and the user-message index (nextUserMessageIndex) that callers need for
|
|
16
|
+
* idempotency key derivation.
|
|
17
|
+
*/
|
|
18
|
+
declare const adaptAgentEvent: (agent: string | undefined, event: HookEvent, transcriptLines: readonly string[]) => AdapterOutput;
|
|
19
|
+
export { adaptAgentEvent };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parseTranscript } from "./parse-transcript.js";
|
|
2
|
+
// Helper: coerce to a non-empty string or null.
|
|
3
|
+
const str = (v) => typeof v === "string" && v ? v : null;
|
|
4
|
+
/**
|
|
5
|
+
* Normalise a raw hook event into session fields, priorAssistant context, and
|
|
6
|
+
* transcript usage flag — varying by agent type.
|
|
7
|
+
*
|
|
8
|
+
* - "codex": reads model/permission_mode/source + last_assistant_message
|
|
9
|
+
* directly from the event (no transcript file).
|
|
10
|
+
* - "claude-code" and generic/unknown: uses parseTranscript for priorAssistant
|
|
11
|
+
* and the user-message index (nextUserMessageIndex) that callers need for
|
|
12
|
+
* idempotency key derivation.
|
|
13
|
+
*/
|
|
14
|
+
const adaptAgentEvent = (agent, event, transcriptLines) => {
|
|
15
|
+
if (agent === "codex") {
|
|
16
|
+
const fields = {};
|
|
17
|
+
const model = str(event.model);
|
|
18
|
+
if (model)
|
|
19
|
+
fields.model = model;
|
|
20
|
+
const permissionMode = str(event.permission_mode);
|
|
21
|
+
if (permissionMode)
|
|
22
|
+
fields.permissionMode = permissionMode;
|
|
23
|
+
const source = str(event.source);
|
|
24
|
+
if (source)
|
|
25
|
+
fields.source = source;
|
|
26
|
+
return {
|
|
27
|
+
sessionFields: fields,
|
|
28
|
+
priorAssistant: typeof event.last_assistant_message === "string"
|
|
29
|
+
? event.last_assistant_message
|
|
30
|
+
: null,
|
|
31
|
+
usesTranscript: false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// claude-code and generic: use the transcript for priorAssistant + message index
|
|
35
|
+
const { priorAssistant } = parseTranscript(transcriptLines);
|
|
36
|
+
const fields = {};
|
|
37
|
+
const permissionMode = str(event.permission_mode);
|
|
38
|
+
if (permissionMode)
|
|
39
|
+
fields.permissionMode = permissionMode;
|
|
40
|
+
const source = str(event.source);
|
|
41
|
+
if (source)
|
|
42
|
+
fields.source = source;
|
|
43
|
+
return {
|
|
44
|
+
sessionFields: fields,
|
|
45
|
+
priorAssistant,
|
|
46
|
+
usesTranscript: true,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
export { adaptAgentEvent };
|
|
50
|
+
//# sourceMappingURL=agent-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-adapter.js","sourceRoot":"","sources":["../../src/_utils/agent-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAaxD,gDAAgD;AAChD,MAAM,GAAG,GAAG,CAAC,CAAU,EAAiB,EAAE,CACxC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAExC;;;;;;;;;GASG;AACH,MAAM,eAAe,GAAG,CACtB,KAAyB,EACzB,KAAgB,EAChB,eAAkC,EACnB,EAAE;IACjB,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,MAAM,MAAM,GAAkB,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,KAAK;YAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;QAChC,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAClD,IAAI,cAAc;YAAE,MAAM,CAAC,cAAc,GAAG,cAAc,CAAC;QAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,MAAM;YAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;QACnC,OAAO;YACL,aAAa,EAAE,MAAM;YACrB,cAAc,EACZ,OAAO,KAAK,CAAC,sBAAsB,KAAK,QAAQ;gBAC9C,CAAC,CAAC,KAAK,CAAC,sBAAsB;gBAC9B,CAAC,CAAC,IAAI;YACV,cAAc,EAAE,KAAK;SACtB,CAAC;IACJ,CAAC;IAED,iFAAiF;IACjF,MAAM,EAAE,cAAc,EAAE,GAAG,eAAe,CAAC,eAAe,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAClD,IAAI,cAAc;QAAE,MAAM,CAAC,cAAc,GAAG,cAAc,CAAC;IAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,MAAM;QAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IACnC,OAAO;QACL,aAAa,EAAE,MAAM;QACrB,cAAc;QACd,cAAc,EAAE,IAAI;KACrB,CAAC;AACJ,CAAC,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -2,7 +2,8 @@ type TurnBodyParams = {
|
|
|
2
2
|
readonly sessionId: string;
|
|
3
3
|
readonly prompt: string;
|
|
4
4
|
readonly priorAssistant: string | null;
|
|
5
|
-
readonly nextUserMessageIndex: number;
|
|
5
|
+
readonly nextUserMessageIndex: number | string;
|
|
6
|
+
readonly additionalContext?: Record<string, unknown>;
|
|
6
7
|
};
|
|
7
8
|
declare const buildTurnBody: (params: TurnBodyParams) => Record<string, unknown>;
|
|
8
9
|
export { buildTurnBody };
|
|
@@ -8,6 +8,9 @@ const buildTurnBody = (params) => ({
|
|
|
8
8
|
: []),
|
|
9
9
|
{ role: "user", content: params.prompt },
|
|
10
10
|
],
|
|
11
|
+
...(params.additionalContext !== undefined
|
|
12
|
+
? { additionalContext: params.additionalContext }
|
|
13
|
+
: {}),
|
|
11
14
|
});
|
|
12
15
|
export { buildTurnBody };
|
|
13
16
|
//# sourceMappingURL=build-turn-body.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-turn-body.js","sourceRoot":"","sources":["../../src/_utils/build-turn-body.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"build-turn-body.js","sourceRoot":"","sources":["../../src/_utils/build-turn-body.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAUtD,MAAM,aAAa,GAAG,CAAC,MAAsB,EAA2B,EAAE,CAAC,CAAC;IAC1E,cAAc,EAAE,MAAM,CAAC,SAAS;IAChC,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,oBAAoB,CAAC;IAC7E,QAAQ,EAAE;QACR,GAAG,CAAC,MAAM,CAAC,cAAc;YACvB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,CAAC;YACzD,CAAC,CAAC,EAAE,CAAC;QACP,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;KACzC;IACD,GAAG,CAAC,MAAM,CAAC,iBAAiB,KAAK,SAAS;QACxC,CAAC,CAAC,EAAE,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,EAAE;QACjD,CAAC,CAAC,EAAE,CAAC;CACR,CAAC,CAAC;AAEH,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
package/dist/_utils/config.d.ts
CHANGED
|
@@ -4,9 +4,9 @@ declare const config: {
|
|
|
4
4
|
exchangeUrl: string;
|
|
5
5
|
turnUrl: string;
|
|
6
6
|
searchUrl: string;
|
|
7
|
-
|
|
7
|
+
listOrgsUrl: string;
|
|
8
8
|
ingestTimeoutMs: number;
|
|
9
9
|
searchTimeoutMs: number;
|
|
10
|
-
|
|
10
|
+
listOrgsTimeoutMs: number;
|
|
11
11
|
};
|
|
12
12
|
export { config };
|
package/dist/_utils/config.js
CHANGED
|
@@ -5,10 +5,10 @@ const config = {
|
|
|
5
5
|
exchangeUrl: `${baseUrl}/api/connect/cli/exchange`,
|
|
6
6
|
turnUrl: `${baseUrl}/api/mcp/route/turn`,
|
|
7
7
|
searchUrl: `${baseUrl}/api/mcp/route/search`,
|
|
8
|
-
|
|
8
|
+
listOrgsUrl: `${baseUrl}/api/mcp/route/listOrgs`,
|
|
9
9
|
ingestTimeoutMs: 8000,
|
|
10
10
|
searchTimeoutMs: 15_000,
|
|
11
|
-
|
|
11
|
+
listOrgsTimeoutMs: 15_000,
|
|
12
12
|
};
|
|
13
13
|
export { config };
|
|
14
14
|
//# sourceMappingURL=config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/_utils/config.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,yBAAyB,CAAC;AAEvE,MAAM,MAAM,GAAG;IACb,OAAO;IACP,UAAU,EAAE,GAAG,OAAO,cAAc;IACpC,WAAW,EAAE,GAAG,OAAO,2BAA2B;IAClD,OAAO,EAAE,GAAG,OAAO,qBAAqB;IACxC,SAAS,EAAE,GAAG,OAAO,uBAAuB;IAC5C,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/_utils/config.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,yBAAyB,CAAC;AAEvE,MAAM,MAAM,GAAG;IACb,OAAO;IACP,UAAU,EAAE,GAAG,OAAO,cAAc;IACpC,WAAW,EAAE,GAAG,OAAO,2BAA2B;IAClD,OAAO,EAAE,GAAG,OAAO,qBAAqB;IACxC,SAAS,EAAE,GAAG,OAAO,uBAAuB;IAC5C,WAAW,EAAE,GAAG,OAAO,yBAAyB;IAChD,eAAe,EAAE,IAAI;IACrB,eAAe,EAAE,MAAM;IACvB,iBAAiB,EAAE,MAAM;CAC1B,CAAC;AAEF,OAAO,EAAE,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type WorkspaceContext = {
|
|
2
|
+
readonly cwd?: string;
|
|
3
|
+
readonly repoName?: string;
|
|
4
|
+
readonly branch?: string;
|
|
5
|
+
readonly commit?: string;
|
|
6
|
+
readonly remoteUrl?: string;
|
|
7
|
+
};
|
|
8
|
+
declare const gitContext: (cwd: string) => WorkspaceContext;
|
|
9
|
+
export { gitContext };
|
|
10
|
+
export type { WorkspaceContext };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { basename } from "node:path";
|
|
3
|
+
const git = (cwd, args) => {
|
|
4
|
+
try {
|
|
5
|
+
return (execFileSync("git", ["-C", cwd, ...args], {
|
|
6
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
7
|
+
timeout: 150,
|
|
8
|
+
encoding: "utf8",
|
|
9
|
+
}).trim() || null);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const stripUserinfo = (url) => url.replace(/^(\w+:\/\/)[^@/]+@/, "$1");
|
|
16
|
+
const gitContext = (cwd) => {
|
|
17
|
+
const toplevel = git(cwd, ["rev-parse", "--show-toplevel"]);
|
|
18
|
+
if (!toplevel)
|
|
19
|
+
return { cwd };
|
|
20
|
+
const branch = git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
21
|
+
const commit = git(cwd, ["rev-parse", "--short", "HEAD"]);
|
|
22
|
+
const remote = git(cwd, ["config", "--get", "remote.origin.url"]);
|
|
23
|
+
return {
|
|
24
|
+
cwd,
|
|
25
|
+
repoName: basename(toplevel),
|
|
26
|
+
...(branch ? { branch } : {}),
|
|
27
|
+
...(commit ? { commit } : {}),
|
|
28
|
+
...(remote ? { remoteUrl: stripUserinfo(remote) } : {}),
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export { gitContext };
|
|
32
|
+
//# sourceMappingURL=git-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-context.js","sourceRoot":"","sources":["../../src/_utils/git-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,IAAc,EAAiB,EAAE;IACzD,IAAI,CAAC;QACH,OAAO,CACL,YAAY,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE;YACxC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;YACnC,OAAO,EAAE,GAAG;YACZ,QAAQ,EAAE,MAAM;SACjB,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAClB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,GAAW,EAAU,EAAE,CAC5C,GAAG,CAAC,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC;AAU1C,MAAM,UAAU,GAAG,CAAC,GAAW,EAAoB,EAAE;IACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC5D,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAClE,OAAO;QACL,GAAG;QACH,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC;QAC5B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxD,CAAC;AACJ,CAAC,CAAC;AAEF,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GlenState } from "./state.js";
|
|
2
|
+
type GlenHeaderInput = {
|
|
3
|
+
readonly key: string;
|
|
4
|
+
readonly state: GlenState | null;
|
|
5
|
+
readonly version: string;
|
|
6
|
+
};
|
|
7
|
+
declare const buildGlenHeaders: ({ key, state, version, }: GlenHeaderInput) => Record<string, string>;
|
|
8
|
+
export { buildGlenHeaders };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const buildGlenHeaders = ({ key, state, version, }) => ({
|
|
2
|
+
"x-api-key": key,
|
|
3
|
+
"x-glen-cli-version": version,
|
|
4
|
+
...(state?.activeOrgId ? { "x-glen-org-id": state.activeOrgId } : {}),
|
|
5
|
+
...(state?.incognito ? { "x-glen-incognito": "true" } : {}),
|
|
6
|
+
});
|
|
7
|
+
export { buildGlenHeaders };
|
|
8
|
+
//# sourceMappingURL=glen-headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glen-headers.js","sourceRoot":"","sources":["../../src/_utils/glen-headers.ts"],"names":[],"mappings":"AAQA,MAAM,gBAAgB,GAAG,CAAC,EACxB,GAAG,EACH,KAAK,EACL,OAAO,GACS,EAA0B,EAAE,CAAC,CAAC;IAC9C,WAAW,EAAE,GAAG;IAChB,oBAAoB,EAAE,OAAO;IAC7B,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACrE,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;CAC5D,CAAC,CAAC;AAEH,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|
package/dist/_utils/http.d.ts
CHANGED
package/dist/_utils/http.js
CHANGED
package/dist/_utils/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/_utils/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/_utils/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAiB1C,MAAM,QAAQ,GAAG,KAAK,EACpB,GAAW,EACX,UAAuB,EAAE,EACe,EAAE;IAC1C,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAE7D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,SAAS;QACzB,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC;QACjD,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,WAAW,GAAG,MAAM,WAAW,CACnC,KAAK,CAAC,GAAG,EAAE;QACT,MAAM;QACN,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,IAAI,EAAE,IAAI,IAAI,IAAI;QAClB,MAAM,EAAE,UAAU,CAAC,MAAM;KAC1B,CAAC,CACH,CAAC;IAEF,IAAI,SAAS,KAAK,SAAS;QAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAErD,IAAI,CAAC,WAAW,CAAC,EAAE;QAAE,OAAO,WAAW,CAAC;IAExC,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAE1B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE;QACjB,IAAI,CAAC;YACH,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAa,CAAC,CAAC,CAAC,IAAI,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,CAAC,gEAAgE;QAC/E,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO;QACL,EAAE,EAAE,IAAI;QACR,KAAK,EAAE;YACL,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,IAAI,EAAE,QAAQ,CAAC,EAAE;YACjB,IAAI;YACJ,OAAO;SACR;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const idempotencyKey: (sessionId: string,
|
|
1
|
+
declare const idempotencyKey: (sessionId: string, userMessageIndexOrTurnId: number | string) => string;
|
|
2
2
|
export { idempotencyKey };
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
// The second argument is a number (user message index from transcript) or a
|
|
3
|
+
// string (turn_id from codex events when no transcript is available). Both are
|
|
4
|
+
// serialised via template literal and separated from sessionId by a null byte
|
|
5
|
+
// to prevent prefix-collision attacks.
|
|
6
|
+
const idempotencyKey = (sessionId, userMessageIndexOrTurnId) => createHash("sha256")
|
|
7
|
+
.update(`${sessionId}\0${userMessageIndexOrTurnId}`)
|
|
4
8
|
.digest("hex");
|
|
5
9
|
export { idempotencyKey };
|
|
6
10
|
//# sourceMappingURL=idempotency-key.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency-key.js","sourceRoot":"","sources":["../../src/_utils/idempotency-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,cAAc,GAAG,
|
|
1
|
+
{"version":3,"file":"idempotency-key.js","sourceRoot":"","sources":["../../src/_utils/idempotency-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,4EAA4E;AAC5E,+EAA+E;AAC/E,8EAA8E;AAC9E,uCAAuC;AACvC,MAAM,cAAc,GAAG,CACrB,SAAiB,EACjB,wBAAyC,EACjC,EAAE,CACV,UAAU,CAAC,QAAQ,CAAC;KACjB,MAAM,CAAC,GAAG,SAAS,KAAK,wBAAwB,EAAE,CAAC;KACnD,MAAM,CAAC,KAAK,CAAC,CAAC;AAEnB,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const formatStateLine = (state) => {
|
|
2
|
+
if (!state)
|
|
3
|
+
return null;
|
|
4
|
+
const org = state.activeOrgSlug ?? state.activeOrgName ?? state.activeOrgId;
|
|
5
|
+
return state.incognito
|
|
6
|
+
? `[glen] org: ${org} · incognito: ON — nothing this session is being recorded`
|
|
7
|
+
: `[glen] org: ${org} · recording`;
|
|
8
|
+
};
|
|
9
|
+
export { formatStateLine };
|
|
10
|
+
//# sourceMappingURL=state-line.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-line.js","sourceRoot":"","sources":["../../src/_utils/state-line.ts"],"names":[],"mappings":"AAEA,MAAM,eAAe,GAAG,CAAC,KAAuB,EAAiB,EAAE;IACjE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,GAAG,GAAG,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,WAAW,CAAC;IAC5E,OAAO,KAAK,CAAC,SAAS;QACpB,CAAC,CAAC,eAAe,GAAG,2DAA2D;QAC/E,CAAC,CAAC,eAAe,GAAG,cAAc,CAAC;AACvC,CAAC,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Result } from "./result.js";
|
|
2
|
+
type GlenState = {
|
|
3
|
+
readonly activeOrgId: string;
|
|
4
|
+
readonly activeOrgSlug: string | null;
|
|
5
|
+
readonly activeOrgName: string | null;
|
|
6
|
+
readonly incognito: boolean;
|
|
7
|
+
};
|
|
8
|
+
declare const statePath: () => string;
|
|
9
|
+
declare const readState: () => Promise<GlenState | null>;
|
|
10
|
+
declare const writeState: (state: GlenState) => Promise<Result<void, unknown>>;
|
|
11
|
+
declare const clearState: () => Promise<void>;
|
|
12
|
+
export { statePath, readState, writeState, clearState };
|
|
13
|
+
export type { GlenState };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fromPromise } from "./result.js";
|
|
5
|
+
const statePath = () => process.env.GLEN_STATE_PATH ?? join(homedir(), ".glen", "state.json");
|
|
6
|
+
const readState = async () => {
|
|
7
|
+
const raw = await fromPromise(readFile(statePath(), "utf-8"));
|
|
8
|
+
if (!raw.ok)
|
|
9
|
+
return null;
|
|
10
|
+
const parsed = await fromPromise(new Promise((resolve) => resolve(JSON.parse(raw.value))));
|
|
11
|
+
if (!parsed.ok)
|
|
12
|
+
return null;
|
|
13
|
+
const d = parsed.value;
|
|
14
|
+
if (!d || typeof d.activeOrgId !== "string")
|
|
15
|
+
return null;
|
|
16
|
+
return {
|
|
17
|
+
activeOrgId: d.activeOrgId,
|
|
18
|
+
activeOrgSlug: typeof d.activeOrgSlug === "string" ? d.activeOrgSlug : null,
|
|
19
|
+
activeOrgName: typeof d.activeOrgName === "string" ? d.activeOrgName : null,
|
|
20
|
+
incognito: d.incognito === true,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
const writeState = async (state) => {
|
|
24
|
+
const filePath = statePath();
|
|
25
|
+
const mk = await fromPromise(mkdir(dirname(filePath), { recursive: true, mode: 0o700 }));
|
|
26
|
+
if (!mk.ok)
|
|
27
|
+
return mk;
|
|
28
|
+
const tmp = `${filePath}.tmp`;
|
|
29
|
+
const w = await fromPromise(writeFile(tmp, JSON.stringify(state, null, 2)));
|
|
30
|
+
if (!w.ok)
|
|
31
|
+
return w;
|
|
32
|
+
const mv = await fromPromise(rename(tmp, filePath));
|
|
33
|
+
if (!mv.ok)
|
|
34
|
+
return mv;
|
|
35
|
+
return { ok: true, value: undefined };
|
|
36
|
+
};
|
|
37
|
+
const clearState = async () => {
|
|
38
|
+
await fromPromise(unlink(statePath()));
|
|
39
|
+
};
|
|
40
|
+
export { statePath, readState, writeState, clearState };
|
|
41
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/_utils/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAU1C,MAAM,SAAS,GAAG,GAAW,EAAE,CAC7B,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;AAExE,MAAM,SAAS,GAAG,KAAK,IAA+B,EAAE;IACtD,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;IAC9D,IAAI,CAAC,GAAG,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,MAAM,GAAG,MAAM,WAAW,CAC9B,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE,CAC/B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAY,CAAC,CAC1C,CACF,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAuC,CAAC;IACzD,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzD,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,aAAa,EAAE,OAAO,CAAC,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI;QAC3E,aAAa,EAAE,OAAO,CAAC,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI;QAC3E,SAAS,EAAE,CAAC,CAAC,SAAS,KAAK,IAAI;KAChC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,KAAK,EAAE,KAAgB,EAAkC,EAAE;IAC5E,MAAM,QAAQ,GAAG,SAAS,EAAE,CAAC;IAC7B,MAAM,EAAE,GAAG,MAAM,WAAW,CAC1B,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAC3D,CAAC;IACF,IAAI,CAAC,EAAE,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IACtB,MAAM,GAAG,GAAG,GAAG,QAAQ,MAAM,CAAC;IAC9B,MAAM,CAAC,GAAG,MAAM,WAAW,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5E,IAAI,CAAC,CAAC,CAAC,EAAE;QAAE,OAAO,CAAC,CAAC;IACpB,MAAM,EAAE,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;IACpD,IAAI,CAAC,EAAE,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IACtB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AACxC,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,KAAK,IAAmB,EAAE;IAC3C,MAAM,WAAW,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const shouldCheck: (now: number) => boolean;
|
|
2
|
+
declare const recordCheck: (now: number, latest: string | null) => void;
|
|
3
|
+
declare const fetchLatest: () => Promise<string | null>;
|
|
4
|
+
declare const newer: (a: string, b: string) => boolean;
|
|
5
|
+
declare const isNpmGlobalInstall: () => boolean;
|
|
6
|
+
declare const runSelfUpdate: () => boolean;
|
|
7
|
+
/** Detached, TTL'd: never blocks the calling turn. Safe to call from every command. */
|
|
8
|
+
declare const maybeSpawnBackgroundUpdate: () => Promise<void>;
|
|
9
|
+
/** 426 from the server: update NOW, then re-exec the (new) global bin with the original args. */
|
|
10
|
+
declare const handle426AndReexec: (argv: string[]) => never;
|
|
11
|
+
export { shouldCheck, recordCheck, fetchLatest, newer, isNpmGlobalInstall, runSelfUpdate, maybeSpawnBackgroundUpdate, handle426AndReexec, };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { cliVersion } from "./version.js";
|
|
6
|
+
const glenDir = () => join(homedir(), ".glen");
|
|
7
|
+
const checkPath = () => join(glenDir(), "update-check.json");
|
|
8
|
+
const lockPath = () => join(glenDir(), "update.lock");
|
|
9
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
10
|
+
const autoUpdateDisabled = () => process.env.GLEN_NO_AUTO_UPDATE === "1" ||
|
|
11
|
+
process.env.CI === "true" ||
|
|
12
|
+
process.env.CI === "1";
|
|
13
|
+
const shouldCheck = (now) => {
|
|
14
|
+
if (autoUpdateDisabled())
|
|
15
|
+
return false;
|
|
16
|
+
try {
|
|
17
|
+
const data = JSON.parse(readFileSync(checkPath(), "utf8"));
|
|
18
|
+
return !data.lastCheckedAt || now - data.lastCheckedAt > DAY_MS;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const recordCheck = (now, latest) => {
|
|
25
|
+
try {
|
|
26
|
+
mkdirSync(glenDir(), { recursive: true });
|
|
27
|
+
writeFileSync(checkPath(), JSON.stringify({ lastCheckedAt: now, latest }));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
/* best-effort */
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const fetchLatest = async () => {
|
|
34
|
+
try {
|
|
35
|
+
const res = await fetch("https://registry.npmjs.org/@tryglen%2fcli/latest", {
|
|
36
|
+
signal: AbortSignal.timeout(4000),
|
|
37
|
+
});
|
|
38
|
+
if (!res.ok)
|
|
39
|
+
return null;
|
|
40
|
+
const body = (await res.json());
|
|
41
|
+
return typeof body.version === "string" ? body.version : null;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const newer = (a, b) => {
|
|
48
|
+
const pa = a.split(".").map(Number);
|
|
49
|
+
const pb = b.split(".").map(Number);
|
|
50
|
+
for (const i of [0, 1, 2]) {
|
|
51
|
+
if ((pa[i] ?? 0) !== (pb[i] ?? 0))
|
|
52
|
+
return (pa[i] ?? 0) > (pb[i] ?? 0);
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
};
|
|
56
|
+
const isNpmGlobalInstall = () => {
|
|
57
|
+
try {
|
|
58
|
+
const prefix = execFileSync("npm", ["prefix", "-g"], {
|
|
59
|
+
encoding: "utf8",
|
|
60
|
+
timeout: 3000,
|
|
61
|
+
}).trim();
|
|
62
|
+
const binPath = realpathSync(process.argv[1] ?? "");
|
|
63
|
+
return binPath.startsWith(realpathSync(prefix));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const runSelfUpdate = () => {
|
|
70
|
+
try {
|
|
71
|
+
mkdirSync(lockPath()); // atomic lock; throws if held
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
execFileSync("npm", ["install", "-g", "@tryglen/cli@latest"], {
|
|
78
|
+
stdio: "ignore",
|
|
79
|
+
timeout: 120_000,
|
|
80
|
+
});
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
rmSync(lockPath(), { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
/** Detached, TTL'd: never blocks the calling turn. Safe to call from every command. */
|
|
91
|
+
const maybeSpawnBackgroundUpdate = async () => {
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
if (!shouldCheck(now))
|
|
94
|
+
return;
|
|
95
|
+
const latest = await fetchLatest();
|
|
96
|
+
recordCheck(now, latest);
|
|
97
|
+
if (!latest || !newer(latest, cliVersion))
|
|
98
|
+
return;
|
|
99
|
+
if (!isNpmGlobalInstall()) {
|
|
100
|
+
process.stderr.write(`[glen] v${latest} is available (you have v${cliVersion}). Update with your package manager.\n`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const child = spawn(process.execPath, [process.argv[1] ?? "glen", "__self-update"], { detached: true, stdio: "ignore" });
|
|
104
|
+
child.unref();
|
|
105
|
+
};
|
|
106
|
+
/** 426 from the server: update NOW, then re-exec the (new) global bin with the original args. */
|
|
107
|
+
const handle426AndReexec = (argv) => {
|
|
108
|
+
if (process.env.GLEN_REEXEC === "1" ||
|
|
109
|
+
autoUpdateDisabled() ||
|
|
110
|
+
!isNpmGlobalInstall() ||
|
|
111
|
+
!runSelfUpdate()) {
|
|
112
|
+
process.stderr.write("[glen] this CLI is below the server's minimum version; run `npm i -g @tryglen/cli@latest`.\n");
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
const r = spawnSync("glen", argv, {
|
|
116
|
+
stdio: "inherit",
|
|
117
|
+
env: { ...process.env, GLEN_REEXEC: "1" },
|
|
118
|
+
});
|
|
119
|
+
process.exit(r.status ?? 1);
|
|
120
|
+
};
|
|
121
|
+
export { shouldCheck, recordCheck, fetchLatest, newer, isNpmGlobalInstall, runSelfUpdate, maybeSpawnBackgroundUpdate, handle426AndReexec, };
|
|
122
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/_utils/update.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
// Resolves package.json relative to this file's location.
|
|
5
|
+
// Works from both src/_utils/ (vitest runs TS directly) and dist/_utils/ (built)
|
|
6
|
+
// because both are exactly two levels deep from the package root.
|
|
7
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "package.json");
|
|
8
|
+
const cliVersion = JSON.parse(readFileSync(pkgPath, "utf8")).version;
|
|
9
|
+
export { cliVersion };
|
|
10
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/_utils/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,0DAA0D;AAC1D,iFAAiF;AACjF,kEAAkE;AAClE,MAAM,OAAO,GAAG,IAAI,CAClB,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EACvC,IAAI,EACJ,IAAI,EACJ,cAAc,CACf,CAAC;AAEF,MAAM,UAAU,GACd,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CACzC,CAAC,OAAO,CAAC;AAEV,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Returns true when the named binary is found on PATH (via `which`).
|
|
4
|
+
* Used by install.ts and doctor.ts to detect coding agents.
|
|
5
|
+
*/
|
|
6
|
+
const isInstalled = (bin) => {
|
|
7
|
+
try {
|
|
8
|
+
execFileSync("which", [bin], { stdio: "ignore" });
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export { isInstalled };
|
|
16
|
+
//# sourceMappingURL=which.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"which.js","sourceRoot":"","sources":["../../src/_utils/which.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;GAGG;AACH,MAAM,WAAW,GAAG,CAAC,GAAW,EAAW,EAAE;IAC3C,IAAI,CAAC;QACH,YAAY,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,CAAC"}
|