cyrus-config-updater 0.2.73 → 0.2.75
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/handlers/githubTokens.d.ts +4 -17
- package/dist/handlers/githubTokens.d.ts.map +1 -1
- package/dist/handlers/githubTokens.js +31 -58
- package/dist/handlers/githubTokens.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/scripts/gh-cyrus.cjs +65 -56
- package/scripts/git-credential-cyrus.cjs +17 -33
- package/scripts/managed-github-auth.cjs +63 -0
|
@@ -37,31 +37,18 @@ export declare function ensureGitHubCredentialHelper(cyrusHome: string): string;
|
|
|
37
37
|
* provisioned from older images keep their baked wrapper until rebuilt;
|
|
38
38
|
* since the wrapper lives in the cyrus user's home, rewrite it here on
|
|
39
39
|
* token pushes — making per-org gh independent of the image rollout.
|
|
40
|
-
* No-op when no wrapper exists (self-host), it already
|
|
41
|
-
*
|
|
40
|
+
* No-op when no wrapper exists (self-host), it already has the managed
|
|
41
|
+
* fence, or it has an unrecognized shape.
|
|
42
42
|
*/
|
|
43
43
|
export declare function ensureGhWrapperSupportsCyrusToken(homeDir?: string): boolean;
|
|
44
|
-
/**
|
|
45
|
-
* Authenticate the `gh` CLI with a pushed installation token.
|
|
46
|
-
*
|
|
47
|
-
* The droplet-local token refresh service used to run `gh auth login` every
|
|
48
|
-
* 20 minutes; with refresh moved to cyrus-hosted, this keeps bare `gh`
|
|
49
|
-
* usage (outside sessions, and sessions on droplet images whose gh wrapper
|
|
50
|
-
* strips GH_TOKEN) authenticated. Multi-org correctness comes from the
|
|
51
|
-
* per-session GH_TOKEN env var; this default uses the first token, which is
|
|
52
|
-
* exact for single-installation teams. Refreshed on every token push.
|
|
53
|
-
*
|
|
54
|
-
* Non-fatal by design — self-host machines may not have `gh` installed.
|
|
55
|
-
*/
|
|
56
|
-
export declare function configureGhCliAuth(token: string): void;
|
|
57
44
|
/**
|
|
58
45
|
* Handle a GitHub installation tokens push from cyrus-hosted.
|
|
59
46
|
*
|
|
60
47
|
* Persists the per-installation tokens to `<cyrusHome>/github-tokens.json`
|
|
61
48
|
* (atomically, mode 0600), ensures the git credential helper is installed
|
|
62
49
|
* so concurrent git operations against different GitHub orgs each
|
|
63
|
-
* authenticate with the right token
|
|
64
|
-
*
|
|
50
|
+
* authenticate with the right token. gh uses per-invocation resolution; no
|
|
51
|
+
* installation token is persisted into native gh hosts/keyring storage.
|
|
65
52
|
*
|
|
66
53
|
* @param rawPayload - Unvalidated payload from the request
|
|
67
54
|
* @param cyrusHome - Path to the Cyrus home directory
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"githubTokens.d.ts","sourceRoot":"","sources":["../../src/handlers/githubTokens.ts"],"names":[],"mappings":"AAaA,OAAO,EACN,KAAK,WAAW,EAGhB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"githubTokens.d.ts","sourceRoot":"","sources":["../../src/handlers/githubTokens.ts"],"names":[],"mappings":"AAaA,OAAO,EACN,KAAK,WAAW,EAGhB,MAAM,aAAa,CAAC;AAqBrB;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA8BtE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iCAAiC,CAChD,OAAO,GAAE,MAAkB,GACzB,OAAO,CA8BT;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,kBAAkB,CACvC,UAAU,EAAE,OAAO,EACnB,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CAyDtB"}
|
|
@@ -12,6 +12,13 @@ function bundledScriptPath(scriptName) {
|
|
|
12
12
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
13
13
|
return join(here, "..", "..", "scripts", scriptName);
|
|
14
14
|
}
|
|
15
|
+
/** Install shared policy before either consumer; retain enrollment on deletion. */
|
|
16
|
+
function ensureManagedAuthPolicy(cyrusHome) {
|
|
17
|
+
const scripts = join(cyrusHome, "scripts");
|
|
18
|
+
mkdirSync(scripts, { recursive: true });
|
|
19
|
+
writeFileSync(join(cyrusHome, "github-auth-managed"), "1\n", { mode: 0o600 });
|
|
20
|
+
copyFileSync(bundledScriptPath("managed-github-auth.cjs"), join(scripts, "managed-github-auth.cjs"));
|
|
21
|
+
}
|
|
15
22
|
/**
|
|
16
23
|
* Install the per-invocation gh token resolver to
|
|
17
24
|
* `<cyrusHome>/scripts/gh-cyrus.cjs`. The droplet's `~/.local/bin/gh`
|
|
@@ -21,6 +28,7 @@ function bundledScriptPath(scriptName) {
|
|
|
21
28
|
* GitHub orgs. Idempotent.
|
|
22
29
|
*/
|
|
23
30
|
export function ensureGhTokenResolver(cyrusHome) {
|
|
31
|
+
ensureManagedAuthPolicy(cyrusHome);
|
|
24
32
|
const scriptDir = join(cyrusHome, "scripts");
|
|
25
33
|
const scriptDest = join(scriptDir, "gh-cyrus.cjs");
|
|
26
34
|
mkdirSync(scriptDir, { recursive: true });
|
|
@@ -45,6 +53,7 @@ export function ensureGhTokenResolver(cyrusHome) {
|
|
|
45
53
|
* Returns the absolute path of the installed helper script.
|
|
46
54
|
*/
|
|
47
55
|
export function ensureGitHubCredentialHelper(cyrusHome) {
|
|
56
|
+
ensureManagedAuthPolicy(cyrusHome);
|
|
48
57
|
const scriptDir = join(cyrusHome, "scripts");
|
|
49
58
|
const scriptDest = join(scriptDir, "git-credential-cyrus.cjs");
|
|
50
59
|
mkdirSync(scriptDir, { recursive: true });
|
|
@@ -82,8 +91,8 @@ export function ensureGitHubCredentialHelper(cyrusHome) {
|
|
|
82
91
|
* provisioned from older images keep their baked wrapper until rebuilt;
|
|
83
92
|
* since the wrapper lives in the cyrus user's home, rewrite it here on
|
|
84
93
|
* token pushes — making per-org gh independent of the image rollout.
|
|
85
|
-
* No-op when no wrapper exists (self-host), it already
|
|
86
|
-
*
|
|
94
|
+
* No-op when no wrapper exists (self-host), it already has the managed
|
|
95
|
+
* fence, or it has an unrecognized shape.
|
|
87
96
|
*/
|
|
88
97
|
export function ensureGhWrapperSupportsCyrusToken(homeDir = homedir()) {
|
|
89
98
|
const wrapperPath = join(homeDir, ".local", "bin", "gh");
|
|
@@ -92,52 +101,35 @@ export function ensureGhWrapperSupportsCyrusToken(homeDir = homedir()) {
|
|
|
92
101
|
const current = readFileSync(wrapperPath, "utf8");
|
|
93
102
|
// Only rewrite the known droplet wrapper shapes (the original
|
|
94
103
|
// strip-everything wrapper and the interim CYRUS_GH_TOKEN one), and
|
|
95
|
-
//
|
|
96
|
-
if (current.includes("
|
|
104
|
+
// upgrade the first-generation resolver wrapper too.
|
|
105
|
+
if (current.includes("Cyrus managed auth v2") ||
|
|
106
|
+
!current.includes("/usr/bin/gh")) {
|
|
97
107
|
return false;
|
|
98
108
|
}
|
|
99
109
|
const updated = `#!/usr/bin/env bash
|
|
100
|
-
# Cyrus
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
RESOLVER="$HOME/.cyrus/scripts/gh-cyrus.cjs"
|
|
110
|
+
# Cyrus managed auth v2: no cached-auth fallback if the resolver is unavailable.
|
|
111
|
+
CYRUS_AUTH_HOME="\${CYRUS_HOME:-$HOME/.cyrus}"
|
|
112
|
+
RESOLVER="$CYRUS_AUTH_HOME/scripts/gh-cyrus.cjs"
|
|
104
113
|
if [ -f "$RESOLVER" ]; then
|
|
105
114
|
exec node "$RESOLVER" "$@"
|
|
106
115
|
fi
|
|
107
|
-
if [ -
|
|
108
|
-
|
|
116
|
+
if [ -e "$CYRUS_AUTH_HOME/github-tokens.json" ] || [ -e "$CYRUS_AUTH_HOME/github-auth-managed" ]; then
|
|
117
|
+
echo "gh-cyrus: managed credential resolver unavailable" >&2
|
|
118
|
+
exit 1
|
|
109
119
|
fi
|
|
110
|
-
exec
|
|
120
|
+
exec /usr/bin/gh "$@"
|
|
111
121
|
`;
|
|
112
122
|
writeFileSync(wrapperPath, updated, { mode: 0o755 });
|
|
113
123
|
return true;
|
|
114
124
|
}
|
|
115
|
-
/**
|
|
116
|
-
* Authenticate the `gh` CLI with a pushed installation token.
|
|
117
|
-
*
|
|
118
|
-
* The droplet-local token refresh service used to run `gh auth login` every
|
|
119
|
-
* 20 minutes; with refresh moved to cyrus-hosted, this keeps bare `gh`
|
|
120
|
-
* usage (outside sessions, and sessions on droplet images whose gh wrapper
|
|
121
|
-
* strips GH_TOKEN) authenticated. Multi-org correctness comes from the
|
|
122
|
-
* per-session GH_TOKEN env var; this default uses the first token, which is
|
|
123
|
-
* exact for single-installation teams. Refreshed on every token push.
|
|
124
|
-
*
|
|
125
|
-
* Non-fatal by design — self-host machines may not have `gh` installed.
|
|
126
|
-
*/
|
|
127
|
-
export function configureGhCliAuth(token) {
|
|
128
|
-
execFileSync("gh", ["auth", "login", "--with-token"], {
|
|
129
|
-
input: token,
|
|
130
|
-
stdio: ["pipe", "ignore", "ignore"],
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
125
|
/**
|
|
134
126
|
* Handle a GitHub installation tokens push from cyrus-hosted.
|
|
135
127
|
*
|
|
136
128
|
* Persists the per-installation tokens to `<cyrusHome>/github-tokens.json`
|
|
137
129
|
* (atomically, mode 0600), ensures the git credential helper is installed
|
|
138
130
|
* so concurrent git operations against different GitHub orgs each
|
|
139
|
-
* authenticate with the right token
|
|
140
|
-
*
|
|
131
|
+
* authenticate with the right token. gh uses per-invocation resolution; no
|
|
132
|
+
* installation token is persisted into native gh hosts/keyring storage.
|
|
141
133
|
*
|
|
142
134
|
* @param rawPayload - Unvalidated payload from the request
|
|
143
135
|
* @param cyrusHome - Path to the Cyrus home directory
|
|
@@ -179,41 +171,22 @@ export async function handleGitHubTokens(rawPayload, cyrusHome) {
|
|
|
179
171
|
}
|
|
180
172
|
try {
|
|
181
173
|
ensureGhTokenResolver(cyrusHome);
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
// Non-fatal: gh falls back to CYRUS_GH_TOKEN / hosts.yml auth.
|
|
185
|
-
console.warn("[githubTokens] gh token resolver install failed:", error instanceof Error ? error.message : String(error));
|
|
186
|
-
}
|
|
187
|
-
try {
|
|
188
|
-
// cyrusHome is <home>/.cyrus on droplets, so the wrapper lives at
|
|
189
|
-
// <parent of cyrusHome>/.local/bin/gh. Using the parent (rather than
|
|
190
|
-
// os.homedir()) keeps this no-op for custom cyrus-home layouts and
|
|
191
|
-
// hermetic in tests.
|
|
192
174
|
ensureGhWrapperSupportsCyrusToken(dirname(cyrusHome));
|
|
193
175
|
}
|
|
194
|
-
catch
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (firstToken) {
|
|
201
|
-
try {
|
|
202
|
-
configureGhCliAuth(firstToken);
|
|
203
|
-
ghAuthConfigured = true;
|
|
204
|
-
}
|
|
205
|
-
catch (error) {
|
|
206
|
-
// Non-fatal: gh may not be installed (self-host), and git auth via
|
|
207
|
-
// the credential helper is unaffected.
|
|
208
|
-
console.warn("[githubTokens] gh CLI auth refresh failed:", error instanceof Error ? error.message : String(error));
|
|
209
|
-
}
|
|
176
|
+
catch {
|
|
177
|
+
// Delivery is not complete when the authentication fence cannot be installed.
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
error: "Failed to configure managed gh authentication",
|
|
181
|
+
};
|
|
210
182
|
}
|
|
211
183
|
return {
|
|
212
184
|
success: true,
|
|
213
185
|
message: "GitHub installation tokens updated successfully",
|
|
214
186
|
data: {
|
|
215
187
|
tokensCount: payload.tokens.length,
|
|
216
|
-
ghAuthConfigured,
|
|
188
|
+
ghAuthConfigured: false,
|
|
189
|
+
ghResolverInstalled: true,
|
|
217
190
|
},
|
|
218
191
|
};
|
|
219
192
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"githubTokens.js","sourceRoot":"","sources":["../../src/handlers/githubTokens.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"githubTokens.js","sourceRoot":"","sources":["../../src/handlers/githubTokens.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -37,6 +37,11 @@ export declare const CyrusConfigPayloadSchema: z.ZodObject<{
|
|
|
37
37
|
claudeDefaultFallbackModel: z.ZodOptional<z.ZodString>;
|
|
38
38
|
geminiDefaultModel: z.ZodOptional<z.ZodString>;
|
|
39
39
|
codexDefaultModel: z.ZodOptional<z.ZodString>;
|
|
40
|
+
codexSandboxMode: z.ZodOptional<z.ZodEnum<{
|
|
41
|
+
"danger-full-access": "danger-full-access";
|
|
42
|
+
"read-only": "read-only";
|
|
43
|
+
"workspace-write": "workspace-write";
|
|
44
|
+
}>>;
|
|
40
45
|
cursorDefaultModel: z.ZodOptional<z.ZodString>;
|
|
41
46
|
cursorDefaultFallbackModel: z.ZodOptional<z.ZodString>;
|
|
42
47
|
opencodeDefaultModel: z.ZodOptional<z.ZodString>;
|
|
@@ -132,6 +137,7 @@ export declare const CyrusConfigPayloadSchema: z.ZodObject<{
|
|
|
132
137
|
}, z.core.$strip>>;
|
|
133
138
|
}, z.core.$strip>>;
|
|
134
139
|
systemWideCert: z.ZodOptional<z.ZodBoolean>;
|
|
140
|
+
writablePaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
135
141
|
logRequests: z.ZodOptional<z.ZodBoolean>;
|
|
136
142
|
}, z.core.$strip>>;
|
|
137
143
|
repositories: z.ZodArray<z.ZodObject<{
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cyrus-config-updater",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.75",
|
|
4
4
|
"description": "Configuration update handlers for Cyrus agent",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"scripts"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
25
|
-
"fastify": "^5.12.
|
|
26
|
-
"zod": "^4.
|
|
27
|
-
"cyrus-core": "0.2.
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
25
|
+
"fastify": "^5.12.7",
|
|
26
|
+
"zod": "^4.6.7",
|
|
27
|
+
"cyrus-core": "0.2.75"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^20.12.7",
|
|
31
31
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
32
|
-
"vitest": "^4.1.
|
|
32
|
+
"vitest": "^4.1.12"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
35
35
|
"config",
|
package/scripts/gh-cyrus.cjs
CHANGED
|
@@ -14,24 +14,18 @@
|
|
|
14
14
|
* 2. A positional repository for `repo view`, `clone`, or `fork`.
|
|
15
15
|
* 3. GH_REPO, then the cwd's `remote.origin.url` (how gh infers "the current
|
|
16
16
|
* repository").
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* repository's org token — covers repo-less commands like `gh api`).
|
|
22
|
-
* 5. The single valid token, when exactly one exists.
|
|
23
|
-
* 6. No token: fall through to gh's own stored auth (hosts.yml).
|
|
24
|
-
*
|
|
25
|
-
* In every case the customer-controlled GITHUB_TOKEN / GH_TOKEN env vars
|
|
26
|
-
* are removed from gh's environment (the wrapper's historical contract);
|
|
27
|
-
* they remain untouched for every other tool in the session.
|
|
17
|
+
* A managed store is authoritative. Explicit owners require an org match;
|
|
18
|
+
* repo-less commands may use a still-current session hint or a single token.
|
|
19
|
+
* Missing/expired/removed credentials refuse before native gh can consult
|
|
20
|
+
* hosts.yml or a keyring. With no store/enrollment, unmanaged auth is untouched.
|
|
28
21
|
*/
|
|
29
22
|
"use strict";
|
|
30
23
|
|
|
31
24
|
const { spawnSync } = require("node:child_process");
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
const {
|
|
26
|
+
loadManagedAuth,
|
|
27
|
+
resolveManagedToken,
|
|
28
|
+
} = require("./managed-github-auth.cjs");
|
|
35
29
|
|
|
36
30
|
/** Extract the owner/org from common GitHub repo references. */
|
|
37
31
|
function ownerFromRepoRef(ref) {
|
|
@@ -118,56 +112,71 @@ function ownerFromCwd() {
|
|
|
118
112
|
return ownerFromRepoRef((result.stdout || "").trim());
|
|
119
113
|
}
|
|
120
114
|
|
|
121
|
-
/**
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const match = valid.find(
|
|
150
|
-
(t) =>
|
|
151
|
-
typeof t.organization === "string" &&
|
|
152
|
-
t.organization.toLowerCase() === lowered,
|
|
115
|
+
/** Explicit REST repo/org targets override cwd and session hints. */
|
|
116
|
+
function ownerFromApi(args, repositoryOwner) {
|
|
117
|
+
if (args[0] !== "api") return "";
|
|
118
|
+
const valueFlags = new Set([
|
|
119
|
+
"--method",
|
|
120
|
+
"-X",
|
|
121
|
+
"--header",
|
|
122
|
+
"-H",
|
|
123
|
+
"--field",
|
|
124
|
+
"-F",
|
|
125
|
+
"--raw-field",
|
|
126
|
+
"-f",
|
|
127
|
+
"--hostname",
|
|
128
|
+
"--input",
|
|
129
|
+
"--jq",
|
|
130
|
+
"-q",
|
|
131
|
+
"--template",
|
|
132
|
+
"-t",
|
|
133
|
+
"--cache",
|
|
134
|
+
]);
|
|
135
|
+
for (let i = 1; i < args.length; i++) {
|
|
136
|
+
if (valueFlags.has(args[i])) {
|
|
137
|
+
i++;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (args[i].startsWith("-")) continue;
|
|
141
|
+
const match = args[i].match(
|
|
142
|
+
/^(?:https:\/\/api\.github\.com)?\/?(?:repos|orgs)\/([^/?#]+)/i,
|
|
153
143
|
);
|
|
154
|
-
if (match) return
|
|
144
|
+
if (!match) return "";
|
|
145
|
+
// gh expands {owner} using its repository context. Keep an unresolved
|
|
146
|
+
// placeholder explicit so it cannot fall back to the single/session token.
|
|
147
|
+
return repositoryOwner
|
|
148
|
+
? match[1].replaceAll("{owner}", repositoryOwner)
|
|
149
|
+
: match[1];
|
|
155
150
|
}
|
|
156
|
-
|
|
157
|
-
if (valid.length === 1) return valid[0].token;
|
|
158
|
-
return undefined;
|
|
151
|
+
return "";
|
|
159
152
|
}
|
|
160
153
|
|
|
161
154
|
function main() {
|
|
162
155
|
const args = process.argv.slice(2);
|
|
163
156
|
|
|
164
|
-
// Strip the customer-controlled token vars from gh's env (historical
|
|
165
|
-
// wrapper contract); set GH_TOKEN only when Cyrus resolved a token.
|
|
166
157
|
const env = { ...process.env };
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
158
|
+
const auth = loadManagedAuth();
|
|
159
|
+
if (auth.managed && args.length === 1 && args[0] === "--version") {
|
|
160
|
+
// The health handler probes installation separately from authentication.
|
|
161
|
+
// Native gh --version does not contact GitHub or inspect stored auth.
|
|
162
|
+
delete env.GITHUB_TOKEN;
|
|
163
|
+
delete env.GH_TOKEN;
|
|
164
|
+
delete env.CYRUS_GH_TOKEN;
|
|
165
|
+
} else if (auth.managed) {
|
|
166
|
+
const repositoryOwner =
|
|
167
|
+
ownerFromArgs(args) || ownerFromRepoRef(env.GH_REPO) || ownerFromCwd();
|
|
168
|
+
const owner = ownerFromApi(args, repositoryOwner) || repositoryOwner;
|
|
169
|
+
const token = resolveManagedToken(auth.tokens, owner, env.CYRUS_GH_TOKEN);
|
|
170
|
+
if (!token) {
|
|
171
|
+
console.error(
|
|
172
|
+
"gh-cyrus: no current managed GitHub credential for this command",
|
|
173
|
+
);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
delete env.GITHUB_TOKEN;
|
|
177
|
+
delete env.CYRUS_GH_TOKEN;
|
|
178
|
+
env.GH_TOKEN = token;
|
|
179
|
+
}
|
|
171
180
|
|
|
172
181
|
const ghBin = process.env.CYRUS_GH_REAL_BIN || "/usr/bin/gh";
|
|
173
182
|
const result = spawnSync(ghBin, args, { stdio: "inherit", env });
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* git-credential-cyrus — git credential helper for multi-org GitHub access.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Node script using the bundled managed-github-auth.cjs policy. Installed at
|
|
6
6
|
* `<cyrusHome>/scripts/git-credential-cyrus.cjs` and wired into git via:
|
|
7
7
|
*
|
|
8
8
|
* git config --global credential."https://github.com".useHttpPath true
|
|
@@ -12,15 +12,13 @@
|
|
|
12
12
|
* For `get` operations against github.com it looks up the org (first path
|
|
13
13
|
* segment) in `<cyrusHome>/github-tokens.json` — the per-installation
|
|
14
14
|
* GitHub App tokens pushed by cyrus-hosted — and prints credentials for a
|
|
15
|
-
* case-insensitive org match.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* case-insensitive org match. Managed misses return quit=true to stop later
|
|
16
|
+
* helpers/keyrings/prompts. Only an absent, never-enrolled store permits
|
|
17
|
+
* unmanaged credential fallback.
|
|
18
18
|
*/
|
|
19
19
|
"use strict";
|
|
20
20
|
|
|
21
21
|
const fs = require("node:fs");
|
|
22
|
-
const os = require("node:os");
|
|
23
|
-
const path = require("node:path");
|
|
24
22
|
|
|
25
23
|
function main() {
|
|
26
24
|
// Only the `get` operation produces credentials; `store`/`erase` are no-ops.
|
|
@@ -46,39 +44,25 @@ function main() {
|
|
|
46
44
|
// With credential.useHttpPath=true git sends e.g. path=owner/repo.git
|
|
47
45
|
const org = (attrs.path || "").split("/")[0] || "";
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
let tokens = [];
|
|
47
|
+
let auth;
|
|
48
|
+
let resolveManagedToken;
|
|
53
49
|
try {
|
|
54
|
-
const
|
|
55
|
-
|
|
50
|
+
const policy = require("./managed-github-auth.cjs");
|
|
51
|
+
auth = policy.loadManagedAuth();
|
|
52
|
+
resolveManagedToken = policy.resolveManagedToken;
|
|
56
53
|
} catch {
|
|
54
|
+
process.stdout.write("quit=true\n");
|
|
57
55
|
return;
|
|
58
56
|
}
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
let match;
|
|
68
|
-
if (org) {
|
|
69
|
-
const lowered = org.toLowerCase();
|
|
70
|
-
match = valid.find(
|
|
71
|
-
(t) =>
|
|
72
|
-
typeof t.organization === "string" &&
|
|
73
|
-
t.organization.toLowerCase() === lowered,
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
if (!match && valid.length === 1) {
|
|
77
|
-
match = valid[0];
|
|
57
|
+
if (!auth.managed) return;
|
|
58
|
+
const token = resolveManagedToken(auth.tokens, org);
|
|
59
|
+
if (!token) {
|
|
60
|
+
// An empty response would permit later keyring helpers and askpass prompts.
|
|
61
|
+
process.stdout.write("quit=true\n");
|
|
62
|
+
return;
|
|
78
63
|
}
|
|
79
|
-
if (!match) return;
|
|
80
64
|
|
|
81
|
-
process.stdout.write(`username=x-access-token\npassword=${
|
|
65
|
+
process.stdout.write(`username=x-access-token\npassword=${token}\n`);
|
|
82
66
|
}
|
|
83
67
|
|
|
84
68
|
main();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Shared by the installed gh resolver and Git credential helper. A present
|
|
3
|
+
// store is authoritative, even when empty/broken. The enrollment marker keeps
|
|
4
|
+
// accidental deletion from silently restoring cached authentication.
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const os = require("node:os");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
|
|
9
|
+
function present(file) {
|
|
10
|
+
try {
|
|
11
|
+
fs.lstatSync(file);
|
|
12
|
+
return true;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return error.code !== "ENOENT";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function loadManagedAuth() {
|
|
19
|
+
const home = process.env.CYRUS_HOME || path.join(os.homedir(), ".cyrus");
|
|
20
|
+
const file = path.join(home, "github-tokens.json");
|
|
21
|
+
const managed =
|
|
22
|
+
present(file) || present(path.join(home, "github-auth-managed"));
|
|
23
|
+
if (!managed) return { managed: false, tokens: [] };
|
|
24
|
+
try {
|
|
25
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
26
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.tokens))
|
|
27
|
+
throw new Error();
|
|
28
|
+
// Refuse the entire malformed snapshot; never reinterpret it as unmanaged.
|
|
29
|
+
if (
|
|
30
|
+
!parsed.tokens.every(
|
|
31
|
+
(t) =>
|
|
32
|
+
t &&
|
|
33
|
+
typeof t.token === "string" &&
|
|
34
|
+
t.token.length > 0 &&
|
|
35
|
+
!/[\s\0]/.test(t.token) &&
|
|
36
|
+
(t.organization === null ||
|
|
37
|
+
(typeof t.organization === "string" &&
|
|
38
|
+
/^[a-z0-9-]+$/i.test(t.organization))) &&
|
|
39
|
+
typeof t.expiresAt === "string" &&
|
|
40
|
+
Number.isFinite(Date.parse(t.expiresAt)),
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
throw new Error();
|
|
44
|
+
return {
|
|
45
|
+
managed: true,
|
|
46
|
+
tokens: parsed.tokens.filter((t) => Date.parse(t.expiresAt) > Date.now()),
|
|
47
|
+
};
|
|
48
|
+
} catch {
|
|
49
|
+
return { managed: true, tokens: [] };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveManagedToken(tokens, owner, sessionToken) {
|
|
54
|
+
// An explicit owner must never borrow a different installation's credential.
|
|
55
|
+
if (owner)
|
|
56
|
+
return tokens.find(
|
|
57
|
+
(t) => t.organization?.toLowerCase() === owner.toLowerCase(),
|
|
58
|
+
)?.token;
|
|
59
|
+
// Session snapshots are only hints, not an independent source of authority.
|
|
60
|
+
if (sessionToken) return tokens.find((t) => t.token === sessionToken)?.token;
|
|
61
|
+
return tokens.length === 1 ? tokens[0].token : undefined;
|
|
62
|
+
}
|
|
63
|
+
module.exports = { loadManagedAuth, resolveManagedToken };
|