cyrus-ai 0.2.73-cypack1502.0 → 0.2.73
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/src/app.js +0 -54
- package/dist/src/app.js.map +1 -1
- package/dist/src/ui/CLIPrompts.d.ts +0 -6
- package/dist/src/ui/CLIPrompts.d.ts.map +1 -1
- package/dist/src/ui/CLIPrompts.js +0 -40
- package/dist/src/ui/CLIPrompts.js.map +1 -1
- package/package.json +8 -15
- package/dist/src/commands/UserCommands.d.ts +0 -62
- package/dist/src/commands/UserCommands.d.ts.map +0 -1
- package/dist/src/commands/UserCommands.js +0 -380
- package/dist/src/commands/UserCommands.js.map +0 -1
- package/dist/src/services/UserCredentialService.d.ts +0 -120
- package/dist/src/services/UserCredentialService.d.ts.map +0 -1
- package/dist/src/services/UserCredentialService.js +0 -261
- package/dist/src/services/UserCredentialService.js.map +0 -1
package/dist/src/app.js
CHANGED
|
@@ -13,7 +13,6 @@ import { RefreshTokenCommand } from "./commands/RefreshTokenCommand.js";
|
|
|
13
13
|
import { SelfAddRepoCommand } from "./commands/SelfAddRepoCommand.js";
|
|
14
14
|
import { SelfAuthCommand } from "./commands/SelfAuthCommand.js";
|
|
15
15
|
import { StartCommand } from "./commands/StartCommand.js";
|
|
16
|
-
import { AddUserCommand, CheckUsersCommand, ListUsersCommand, RemoveUserCommand, } from "./commands/UserCommands.js";
|
|
17
16
|
import { createErrorReporter } from "./services/createErrorReporter.js";
|
|
18
17
|
// Get the directory of the current module for reading package.json
|
|
19
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -104,59 +103,6 @@ program
|
|
|
104
103
|
}
|
|
105
104
|
await new SelfAddRepoCommand(app).execute(args);
|
|
106
105
|
});
|
|
107
|
-
// Per-Linear-user credentials (multi-user self-host)
|
|
108
|
-
program
|
|
109
|
-
.command("add-user")
|
|
110
|
-
.description("Map a Linear user to their own Claude credential and GitHub token so sessions they trigger run as them. Secrets come from a hidden prompt, a file or an env var name — never from the command line.")
|
|
111
|
-
.option("--linear-email <email>", "Linear email to resolve the user ID")
|
|
112
|
-
.option("--linear-user-id <id>", "Linear user ID (skips email lookup)")
|
|
113
|
-
.option("--name <name>", "Display name (defaults to the Linear name)")
|
|
114
|
-
.option("--git-name <name>", "Git author/committer name (defaults to the GitHub profile name)")
|
|
115
|
-
.option("--git-email <email>", "Git author/committer email (defaults to the GitHub noreply address)")
|
|
116
|
-
.option("--claude-token-file <path>", "Read the Claude OAuth token / API key from a file")
|
|
117
|
-
.option("--claude-token-env <VAR>", "Reference an env var (set in ~/.cyrus/.env) instead of storing a file")
|
|
118
|
-
.option("--claude-kind <kind>", "Force the credential type: oauth | api-key (auto-detected from the prefix)")
|
|
119
|
-
.option("--github-token-file <path>", "Read the GitHub personal access token from a file")
|
|
120
|
-
.option("--github-token-env <VAR>", "Reference an env var instead of storing a file")
|
|
121
|
-
.option("--github-only", "Configure personal GitHub identity while keeping existing model authentication")
|
|
122
|
-
.option("--skip-claude-check", "Skip the live one-turn Claude verification")
|
|
123
|
-
.option("--skip-github-check", "Skip verifying the GitHub token against api.github.com")
|
|
124
|
-
.option("--workspace <id|slug|name>", "Linear workspace to look the user up in (when several are configured)")
|
|
125
|
-
.option("--force", "Replace an existing mapping for this user")
|
|
126
|
-
.action(async (cmdOpts) => {
|
|
127
|
-
const opts = program.opts();
|
|
128
|
-
const app = new Application(opts.cyrusHome, opts.envFile, packageJson.version, errorReporter);
|
|
129
|
-
await new AddUserCommand(app).execute([], cmdOpts);
|
|
130
|
-
await app.shutdown();
|
|
131
|
-
});
|
|
132
|
-
program
|
|
133
|
-
.command("list-users")
|
|
134
|
-
.description("List Linear users mapped to their own credentials (references and fingerprints only)")
|
|
135
|
-
.action(async () => {
|
|
136
|
-
const opts = program.opts();
|
|
137
|
-
const app = new Application(opts.cyrusHome, opts.envFile, packageJson.version, errorReporter);
|
|
138
|
-
await new ListUsersCommand(app).execute();
|
|
139
|
-
await app.shutdown();
|
|
140
|
-
});
|
|
141
|
-
program
|
|
142
|
-
.command("check-users")
|
|
143
|
-
.description("Verify every mapped user's credential references resolve; --live also calls GitHub and Claude")
|
|
144
|
-
.option("--live", "Also verify each token against GitHub and with a one-turn Claude round trip")
|
|
145
|
-
.action(async (cmdOpts) => {
|
|
146
|
-
const opts = program.opts();
|
|
147
|
-
const app = new Application(opts.cyrusHome, opts.envFile, packageJson.version, errorReporter);
|
|
148
|
-
await new CheckUsersCommand(app).execute([], cmdOpts);
|
|
149
|
-
await app.shutdown();
|
|
150
|
-
});
|
|
151
|
-
program
|
|
152
|
-
.command("remove-user <linear-user-id-or-email>")
|
|
153
|
-
.description("Remove a mapped Linear user and delete their stored secret files")
|
|
154
|
-
.action(async (target) => {
|
|
155
|
-
const opts = program.opts();
|
|
156
|
-
const app = new Application(opts.cyrusHome, opts.envFile, packageJson.version, errorReporter);
|
|
157
|
-
await new RemoveUserCommand(app).execute([target]);
|
|
158
|
-
await app.shutdown();
|
|
159
|
-
});
|
|
160
106
|
// Parse and execute
|
|
161
107
|
(async () => {
|
|
162
108
|
try {
|
package/dist/src/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/app.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,12 +6,6 @@ export declare namespace CLIPrompts {
|
|
|
6
6
|
* Ask a question and return the user's answer
|
|
7
7
|
*/
|
|
8
8
|
function ask(prompt: string): Promise<string>;
|
|
9
|
-
/**
|
|
10
|
-
* Ask for a secret without echoing it. Keystrokes are masked so the value
|
|
11
|
-
* never lands in the terminal scrollback or shell history. Falls back to
|
|
12
|
-
* reading one line from stdin when stdin is not a TTY (piped input).
|
|
13
|
-
*/
|
|
14
|
-
function askSecret(prompt: string): Promise<string>;
|
|
15
9
|
/**
|
|
16
10
|
* Ask a yes/no question
|
|
17
11
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CLIPrompts.d.ts","sourceRoot":"","sources":["../../../src/ui/CLIPrompts.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,yBAAiB,UAAU,CAAC;IAC3B;;OAEG;IACH,SAAsB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYzD;IAED
|
|
1
|
+
{"version":3,"file":"CLIPrompts.d.ts","sourceRoot":"","sources":["../../../src/ui/CLIPrompts.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,yBAAiB,UAAU,CAAC;IAC3B;;OAEG;IACH,SAAsB,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYzD;IAED;;OAEG;IACH,SAAsB,OAAO,CAC5B,MAAM,EAAE,MAAM,EACd,YAAY,UAAQ,GAClB,OAAO,CAAC,OAAO,CAAC,CASlB;IAED;;OAEG;IACH,SAAsB,IAAI,CACzB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBxB;CACD"}
|
|
@@ -20,46 +20,6 @@ export var CLIPrompts;
|
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
CLIPrompts.ask = ask;
|
|
23
|
-
/**
|
|
24
|
-
* Ask for a secret without echoing it. Keystrokes are masked so the value
|
|
25
|
-
* never lands in the terminal scrollback or shell history. Falls back to
|
|
26
|
-
* reading one line from stdin when stdin is not a TTY (piped input).
|
|
27
|
-
*/
|
|
28
|
-
async function askSecret(prompt) {
|
|
29
|
-
if (!process.stdin.isTTY) {
|
|
30
|
-
// Piped: read a single line, no echo concerns.
|
|
31
|
-
return (await CLIPrompts.ask(prompt)).trim();
|
|
32
|
-
}
|
|
33
|
-
const rl = readline.createInterface({
|
|
34
|
-
input: process.stdin,
|
|
35
|
-
output: process.stdout,
|
|
36
|
-
terminal: true,
|
|
37
|
-
});
|
|
38
|
-
// Mute echo while the secret is typed (readline writes the prompt itself).
|
|
39
|
-
const mutable = rl;
|
|
40
|
-
const originalWrite = mutable._writeToOutput;
|
|
41
|
-
let muted = false;
|
|
42
|
-
mutable._writeToOutput = (s) => {
|
|
43
|
-
if (!muted) {
|
|
44
|
-
originalWrite?.call(rl, s);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
// Show a bullet per keystroke; swallow everything else.
|
|
48
|
-
if (s.includes("\n"))
|
|
49
|
-
mutable.output.write("\n");
|
|
50
|
-
else if (s.length === 1)
|
|
51
|
-
mutable.output.write("•");
|
|
52
|
-
};
|
|
53
|
-
return new Promise((resolve) => {
|
|
54
|
-
rl.question(prompt, (answer) => {
|
|
55
|
-
muted = false;
|
|
56
|
-
rl.close();
|
|
57
|
-
resolve(answer.trim());
|
|
58
|
-
});
|
|
59
|
-
muted = true;
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
CLIPrompts.askSecret = askSecret;
|
|
63
23
|
/**
|
|
64
24
|
* Ask a yes/no question
|
|
65
25
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CLIPrompts.js","sourceRoot":"","sources":["../../../src/ui/CLIPrompts.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC;;GAEG;AACH,MAAM,KAAW,UAAU,
|
|
1
|
+
{"version":3,"file":"CLIPrompts.js","sourceRoot":"","sources":["../../../src/ui/CLIPrompts.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC;;GAEG;AACH,MAAM,KAAW,UAAU,CA0D1B;AA1DD,WAAiB,UAAU;IAC1B;;OAEG;IACI,KAAK,UAAU,GAAG,CAAC,MAAc;QACvC,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;YACnC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;SACtB,CAAC,CAAC;QAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC9B,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;gBAC9B,EAAE,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACxB,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACJ,CAAC;IAZqB,WAAA,GAAG,MAYxB,CAAA;IAED;;OAEG;IACI,KAAK,UAAU,OAAO,CAC5B,MAAc,EACd,YAAY,GAAG,KAAK;QAEpB,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;QACtD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QAErD,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,OAAO,YAAY,CAAC;QACrB,CAAC;QAED,OAAO,MAAM,CAAC,WAAW,EAAE,KAAK,GAAG,IAAI,MAAM,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC;IACvE,CAAC;IAZqB,WAAA,OAAO,UAY5B,CAAA;IAED;;OAEG;IACI,KAAK,UAAU,IAAI,CACzB,KAAa,EACb,OAAiB;QAEjB,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAE5B,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;YACjC,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAEpC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YACnE,OAAO,IAAI,CAAC;QACb,CAAC;QAED,OAAO,MAAM,GAAG,CAAC,CAAC;IACnB,CAAC;IAnBqB,WAAA,IAAI,OAmBzB,CAAA;AACF,CAAC,EA1DgB,UAAU,KAAV,UAAU,QA0D1B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cyrus-ai",
|
|
3
|
-
"version": "0.2.73
|
|
3
|
+
"version": "0.2.73",
|
|
4
4
|
"description": "AI-powered Linear issue automation using Claude",
|
|
5
5
|
"main": "dist/src/app.js",
|
|
6
6
|
"types": "dist/src/app.d.ts",
|
|
@@ -35,18 +35,18 @@
|
|
|
35
35
|
"commander": "^14.0.2",
|
|
36
36
|
"dotenv": "^16.5.0",
|
|
37
37
|
"express": "^5.2.1",
|
|
38
|
-
"fastify": "^5.12.
|
|
38
|
+
"fastify": "^5.12.5",
|
|
39
39
|
"file-type": "^21.0.0",
|
|
40
40
|
"fs-extra": "^11.3.0",
|
|
41
41
|
"node-fetch": "^2.7.0",
|
|
42
42
|
"open": "^10.0.0",
|
|
43
43
|
"zod": "^4.3.5",
|
|
44
|
-
"cyrus-
|
|
45
|
-
"cyrus-
|
|
46
|
-
"cyrus-
|
|
47
|
-
"cyrus-edge-worker": "0.2.73
|
|
48
|
-
"cyrus-
|
|
49
|
-
"cyrus-
|
|
44
|
+
"cyrus-cloudflare-tunnel-client": "0.2.73",
|
|
45
|
+
"cyrus-claude-runner": "0.2.73",
|
|
46
|
+
"cyrus-config-updater": "0.2.73",
|
|
47
|
+
"cyrus-edge-worker": "0.2.73",
|
|
48
|
+
"cyrus-slack-event-transport": "0.2.73",
|
|
49
|
+
"cyrus-core": "0.2.73"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "^20.0.0",
|
|
@@ -55,13 +55,6 @@
|
|
|
55
55
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
56
56
|
"vitest": "^4.1.11"
|
|
57
57
|
},
|
|
58
|
-
"cyrusTestRelease": {
|
|
59
|
-
"schemaVersion": 1,
|
|
60
|
-
"repository": "https://github.com/cyrusagents/cyrus",
|
|
61
|
-
"sourceSha": "bd45d03ae855d4148257cfeb93d026f88d886626",
|
|
62
|
-
"version": "0.2.73-cypack1502.0",
|
|
63
|
-
"channel": "test-cypack1502"
|
|
64
|
-
},
|
|
65
58
|
"scripts": {
|
|
66
59
|
"build": "tsgo",
|
|
67
60
|
"start": "node dist/src/app.js",
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-Linear-user credential provisioning (multi-user self-host, CYPACK-1502).
|
|
3
|
-
*
|
|
4
|
-
* cyrus add-user — map a Linear user to their Claude credential + GitHub token
|
|
5
|
-
* cyrus list-users — show the mapping (references + fingerprints, never secrets)
|
|
6
|
-
* cyrus check-users — verify every reference resolves (optionally live provider checks)
|
|
7
|
-
* cyrus remove-user — drop a mapping and its stored secrets
|
|
8
|
-
*
|
|
9
|
-
* Secrets are taken from a masked prompt, a protected file or an env var name —
|
|
10
|
-
* never from a literal command-line argument (shell history) — and are stored
|
|
11
|
-
* in the shared github-tokens.json store (GitHub) or protected user-credentials
|
|
12
|
-
* files (Claude). config.json receives references only.
|
|
13
|
-
*/
|
|
14
|
-
import { type ClaudeCredentialKind } from "../services/UserCredentialService.js";
|
|
15
|
-
import { BaseCommand } from "./ICommand.js";
|
|
16
|
-
export interface AddUserOptions {
|
|
17
|
-
githubOnly?: boolean;
|
|
18
|
-
linearUserId?: string;
|
|
19
|
-
linearEmail?: string;
|
|
20
|
-
name?: string;
|
|
21
|
-
gitName?: string;
|
|
22
|
-
gitEmail?: string;
|
|
23
|
-
claudeTokenFile?: string;
|
|
24
|
-
claudeTokenEnv?: string;
|
|
25
|
-
claudeKind?: "oauth" | "api-key";
|
|
26
|
-
githubTokenFile?: string;
|
|
27
|
-
githubTokenEnv?: string;
|
|
28
|
-
skipClaudeCheck?: boolean;
|
|
29
|
-
skipGithubCheck?: boolean;
|
|
30
|
-
force?: boolean;
|
|
31
|
-
workspace?: string;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Real provider check: run a one-turn Claude session with ONLY the user's
|
|
35
|
-
* credential in the child environment. Succeeds when the SDK returns a
|
|
36
|
-
* `result` message with `subtype: "success"`. Returns a secret-free error
|
|
37
|
-
* otherwise.
|
|
38
|
-
*/
|
|
39
|
-
export declare function verifyClaudeCredentialLive(cyrusHome: string, kind: ClaudeCredentialKind, secret: string): Promise<{
|
|
40
|
-
ok: true;
|
|
41
|
-
model?: string;
|
|
42
|
-
} | {
|
|
43
|
-
ok: false;
|
|
44
|
-
error: string;
|
|
45
|
-
}>;
|
|
46
|
-
export declare class AddUserCommand extends BaseCommand {
|
|
47
|
-
private service;
|
|
48
|
-
execute(_args: string[], options?: AddUserOptions): Promise<void>;
|
|
49
|
-
}
|
|
50
|
-
export declare class ListUsersCommand extends BaseCommand {
|
|
51
|
-
execute(): Promise<void>;
|
|
52
|
-
}
|
|
53
|
-
export declare class CheckUsersCommand extends BaseCommand {
|
|
54
|
-
execute(_args: string[], options?: {
|
|
55
|
-
live?: boolean;
|
|
56
|
-
}): Promise<void>;
|
|
57
|
-
}
|
|
58
|
-
export declare class RemoveUserCommand extends BaseCommand {
|
|
59
|
-
private service;
|
|
60
|
-
execute(args: string[]): Promise<void>;
|
|
61
|
-
}
|
|
62
|
-
//# sourceMappingURL=UserCommands.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"UserCommands.d.ts","sourceRoot":"","sources":["../../../src/commands/UserCommands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,OAAO,EAGN,KAAK,oBAAoB,EAIzB,MAAM,sCAAsC,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AA6BD;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC/C,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,oBAAoB,EAC1B,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAoDtE;AAkCD,qBAAa,cAAe,SAAQ,WAAW;IAC9C,OAAO,CAAC,OAAO,CAAiD;IAE1D,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiM1E;CACD;AAED,qBAAa,gBAAiB,SAAQ,WAAW;IAC1C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAqC7B;CACD;AAED,qBAAa,iBAAkB,SAAQ,WAAW;IAC3C,OAAO,CACZ,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAO,GAC9B,OAAO,CAAC,IAAI,CAAC,CAiFf;CACD;AAED,qBAAa,iBAAkB,SAAQ,WAAW;IACjD,OAAO,CAAC,OAAO,CAAiD;IAE1D,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAyC3C;CACD"}
|
|
@@ -1,380 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-Linear-user credential provisioning (multi-user self-host, CYPACK-1502).
|
|
3
|
-
*
|
|
4
|
-
* cyrus add-user — map a Linear user to their Claude credential + GitHub token
|
|
5
|
-
* cyrus list-users — show the mapping (references + fingerprints, never secrets)
|
|
6
|
-
* cyrus check-users — verify every reference resolves (optionally live provider checks)
|
|
7
|
-
* cyrus remove-user — drop a mapping and its stored secrets
|
|
8
|
-
*
|
|
9
|
-
* Secrets are taken from a masked prompt, a protected file or an env var name —
|
|
10
|
-
* never from a literal command-line argument (shell history) — and are stored
|
|
11
|
-
* in the shared github-tokens.json store (GitHub) or protected user-credentials
|
|
12
|
-
* files (Claude). config.json receives references only.
|
|
13
|
-
*/
|
|
14
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
15
|
-
import { tmpdir } from "node:os";
|
|
16
|
-
import { join } from "node:path";
|
|
17
|
-
import { ClaudeRunner } from "cyrus-claude-runner";
|
|
18
|
-
import { ALTERNATIVE_AUTH_ENV_KEYS, credentialFingerprint, readCredentialRef, readGitHubCredentialRef, resolvePrompterCredentialPolicy, } from "cyrus-core";
|
|
19
|
-
import { CLAUDE_API_KEY_FILE, CLAUDE_OAUTH_TOKEN_FILE, detectClaudeCredentialKind, UserCredentialService, } from "../services/UserCredentialService.js";
|
|
20
|
-
import { CLIPrompts } from "../ui/CLIPrompts.js";
|
|
21
|
-
import { BaseCommand } from "./ICommand.js";
|
|
22
|
-
/**
|
|
23
|
-
* Resolve the Linear workspace token used for user lookups: the only
|
|
24
|
-
* workspace when there is one, else the one matching `--workspace`.
|
|
25
|
-
*/
|
|
26
|
-
function pickLinearToken(config, workspace) {
|
|
27
|
-
const entries = Object.entries(config.linearWorkspaces ?? {});
|
|
28
|
-
if (entries.length === 0)
|
|
29
|
-
return undefined;
|
|
30
|
-
const match = workspace
|
|
31
|
-
? entries.find(([id, ws]) => id === workspace ||
|
|
32
|
-
ws.linearWorkspaceSlug === workspace ||
|
|
33
|
-
ws.linearWorkspaceName === workspace)
|
|
34
|
-
: entries.length === 1
|
|
35
|
-
? entries[0]
|
|
36
|
-
: undefined;
|
|
37
|
-
if (!match)
|
|
38
|
-
return undefined;
|
|
39
|
-
return {
|
|
40
|
-
token: match[1].linearToken,
|
|
41
|
-
name: match[1].linearWorkspaceName ?? match[0],
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Real provider check: run a one-turn Claude session with ONLY the user's
|
|
46
|
-
* credential in the child environment. Succeeds when the SDK returns a
|
|
47
|
-
* `result` message with `subtype: "success"`. Returns a secret-free error
|
|
48
|
-
* otherwise.
|
|
49
|
-
*/
|
|
50
|
-
export async function verifyClaudeCredentialLive(cyrusHome, kind, secret) {
|
|
51
|
-
const workDir = mkdtempSync(join(tmpdir(), "cyrus-claude-check-"));
|
|
52
|
-
try {
|
|
53
|
-
const env = kind === "oauthToken"
|
|
54
|
-
? { CLAUDE_CODE_OAUTH_TOKEN: secret }
|
|
55
|
-
: { ANTHROPIC_API_KEY: secret };
|
|
56
|
-
const omitEnv = kind === "oauthToken"
|
|
57
|
-
? ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"]
|
|
58
|
-
: ["CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_AUTH_TOKEN"];
|
|
59
|
-
const runner = new ClaudeRunner({
|
|
60
|
-
cyrusHome,
|
|
61
|
-
workingDirectory: workDir,
|
|
62
|
-
model: "haiku",
|
|
63
|
-
maxTurns: 1,
|
|
64
|
-
tools: [],
|
|
65
|
-
allowedTools: [],
|
|
66
|
-
strictMcpConfig: true,
|
|
67
|
-
additionalEnv: env,
|
|
68
|
-
omitEnv: [...omitEnv, ...ALTERNATIVE_AUTH_ENV_KEYS],
|
|
69
|
-
});
|
|
70
|
-
await runner.start("Reply with the single word OK.");
|
|
71
|
-
const result = runner
|
|
72
|
-
.getMessages()
|
|
73
|
-
.find((m) => m.type === "result");
|
|
74
|
-
if (!result) {
|
|
75
|
-
return { ok: false, error: "Claude returned no result message" };
|
|
76
|
-
}
|
|
77
|
-
if (result.subtype === "success") {
|
|
78
|
-
const modelUsage = result
|
|
79
|
-
.modelUsage;
|
|
80
|
-
return {
|
|
81
|
-
ok: true,
|
|
82
|
-
model: modelUsage ? Object.keys(modelUsage)[0] : undefined,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
const errors = result.errors ?? [];
|
|
86
|
-
return {
|
|
87
|
-
ok: false,
|
|
88
|
-
error: `${result.subtype}${errors.length ? `: ${errors.join("; ")}` : ""}`,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
return {
|
|
93
|
-
ok: false,
|
|
94
|
-
error: error instanceof Error ? error.message : String(error),
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
finally {
|
|
98
|
-
rmSync(workDir, { recursive: true, force: true });
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Obtain a secret from one of the accepted sources (file, env var name,
|
|
103
|
-
* masked prompt). Never from argv.
|
|
104
|
-
*/
|
|
105
|
-
async function obtainSecret(label, source) {
|
|
106
|
-
if (source.file && source.env) {
|
|
107
|
-
throw new Error(`Provide either --${label}-token-file or --${label}-token-env, not both`);
|
|
108
|
-
}
|
|
109
|
-
if (source.file) {
|
|
110
|
-
return { secret: UserCredentialService.readSecretFromFile(source.file) };
|
|
111
|
-
}
|
|
112
|
-
if (source.env) {
|
|
113
|
-
const read = readCredentialRef({ env: source.env });
|
|
114
|
-
if ("error" in read) {
|
|
115
|
-
throw new Error(`${read.error} — set it in ~/.cyrus/.env (or the environment) before running add-user`);
|
|
116
|
-
}
|
|
117
|
-
return { secret: read.value, ref: { env: source.env } };
|
|
118
|
-
}
|
|
119
|
-
const typed = await CLIPrompts.askSecret(`Paste the ${label} token (input hidden): `);
|
|
120
|
-
if (!typed)
|
|
121
|
-
throw new Error(`No ${label} token provided`);
|
|
122
|
-
return { secret: typed };
|
|
123
|
-
}
|
|
124
|
-
export class AddUserCommand extends BaseCommand {
|
|
125
|
-
service = new UserCredentialService(this.app.cyrusHome);
|
|
126
|
-
async execute(_args, options = {}) {
|
|
127
|
-
if (!this.app.config.exists()) {
|
|
128
|
-
this.exitWithError("No edge configuration found. Run `cyrus auth` or `cyrus self-auth-linear` first.");
|
|
129
|
-
}
|
|
130
|
-
const config = this.app.config.load();
|
|
131
|
-
console.log("\n👤 Adding a Linear user to per-prompter credentials");
|
|
132
|
-
this.logDivider();
|
|
133
|
-
// ---- 1. Resolve the Linear user -------------------------------------
|
|
134
|
-
let linearUserId = options.linearUserId?.trim();
|
|
135
|
-
let displayName = options.name?.trim();
|
|
136
|
-
let linearEmail = options.linearEmail?.trim();
|
|
137
|
-
if (!linearUserId && !linearEmail) {
|
|
138
|
-
linearEmail = await CLIPrompts.ask("Linear email of the engineer: ");
|
|
139
|
-
}
|
|
140
|
-
const linearWs = pickLinearToken(config, options.workspace);
|
|
141
|
-
if (linearWs) {
|
|
142
|
-
try {
|
|
143
|
-
const user = await UserCredentialService.lookupLinearUser(linearWs.token, {
|
|
144
|
-
id: linearUserId,
|
|
145
|
-
email: linearUserId ? undefined : linearEmail,
|
|
146
|
-
});
|
|
147
|
-
linearUserId = user.id;
|
|
148
|
-
linearEmail = user.email;
|
|
149
|
-
displayName = displayName || user.displayName || user.name;
|
|
150
|
-
if (!user.active) {
|
|
151
|
-
console.log("⚠️ Linear reports this user as inactive.");
|
|
152
|
-
}
|
|
153
|
-
this.logSuccess(`Linear user resolved: ${displayName} <${user.email}> (${user.id}) in ${linearWs.name}`);
|
|
154
|
-
}
|
|
155
|
-
catch (error) {
|
|
156
|
-
if (!linearUserId) {
|
|
157
|
-
this.exitWithError(`Could not resolve the Linear user: ${error.message}`);
|
|
158
|
-
}
|
|
159
|
-
console.log(`⚠️ Linear lookup failed (${error.message}); continuing with the ID you provided.`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
else if (!linearUserId) {
|
|
163
|
-
this.exitWithError("No Linear workspace token available to look up an email. Pass --linear-user-id (and --name) instead, or --workspace to pick one of several workspaces.");
|
|
164
|
-
}
|
|
165
|
-
if (!linearUserId) {
|
|
166
|
-
this.exitWithError("A Linear user ID is required.");
|
|
167
|
-
}
|
|
168
|
-
displayName = displayName || linearEmail || linearUserId;
|
|
169
|
-
if (config.linearUsers?.[linearUserId] && !options.force) {
|
|
170
|
-
this.exitWithError(`${displayName} (${linearUserId}) is already mapped. Re-run with --force to replace the credentials.`);
|
|
171
|
-
}
|
|
172
|
-
// ---- 2. Claude credential -------------------------------------------
|
|
173
|
-
let claude;
|
|
174
|
-
let claudeKind;
|
|
175
|
-
if (options.githubOnly &&
|
|
176
|
-
(options.claudeTokenFile || options.claudeTokenEnv || options.claudeKind)) {
|
|
177
|
-
this.exitWithError("--github-only cannot be combined with Claude credential options.");
|
|
178
|
-
}
|
|
179
|
-
if (!options.githubOnly) {
|
|
180
|
-
claude = await obtainSecret("claude", {
|
|
181
|
-
file: options.claudeTokenFile,
|
|
182
|
-
env: options.claudeTokenEnv,
|
|
183
|
-
});
|
|
184
|
-
claudeKind =
|
|
185
|
-
options.claudeKind === "oauth"
|
|
186
|
-
? "oauthToken"
|
|
187
|
-
: options.claudeKind === "api-key"
|
|
188
|
-
? "apiKey"
|
|
189
|
-
: detectClaudeCredentialKind(claude.secret);
|
|
190
|
-
if (!claudeKind) {
|
|
191
|
-
const choice = await CLIPrompts.menu("Could not detect the Claude credential type from its prefix. Which is it?", [
|
|
192
|
-
"Claude Code OAuth token (from `claude setup-token`)",
|
|
193
|
-
"Anthropic console API key",
|
|
194
|
-
]);
|
|
195
|
-
claudeKind = choice === 1 ? "apiKey" : "oauthToken";
|
|
196
|
-
}
|
|
197
|
-
console.log(` Claude credential: ${claudeKind === "oauthToken" ? "OAuth token (claude setup-token)" : "API key"} · fingerprint ${credentialFingerprint(claude.secret)}`);
|
|
198
|
-
if (!options.skipClaudeCheck) {
|
|
199
|
-
console.log(" Verifying the Claude credential with a one-turn haiku round trip…");
|
|
200
|
-
const check = await verifyClaudeCredentialLive(this.app.cyrusHome, claudeKind, claude.secret);
|
|
201
|
-
if (!check.ok) {
|
|
202
|
-
this.exitWithError(`Claude credential check failed: ${check.error}. Re-run with --skip-claude-check to store it anyway.`);
|
|
203
|
-
}
|
|
204
|
-
this.logSuccess(`Claude credential works${check.model ? ` (model ${check.model})` : ""}`);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
// ---- 3. GitHub token --------------------------------------------------
|
|
208
|
-
const github = await obtainSecret("github", {
|
|
209
|
-
file: options.githubTokenFile,
|
|
210
|
-
env: options.githubTokenEnv,
|
|
211
|
-
});
|
|
212
|
-
let actor;
|
|
213
|
-
if (!options.skipGithubCheck) {
|
|
214
|
-
try {
|
|
215
|
-
actor = await UserCredentialService.verifyGitHubToken(github.secret);
|
|
216
|
-
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
this.exitWithError(`${error.message} Re-run with --skip-github-check to store it anyway.`);
|
|
219
|
-
}
|
|
220
|
-
this.logSuccess(`GitHub token verified: acts as @${actor.login}${actor.name ? ` (${actor.name})` : ""} · fingerprint ${credentialFingerprint(github.secret)}${actor.tokenScopes ? ` · scopes: ${actor.tokenScopes}` : " · fine-grained token"}`);
|
|
221
|
-
}
|
|
222
|
-
const gitAuthorName = options.gitName?.trim() || actor?.name || displayName;
|
|
223
|
-
const gitAuthorEmail = options.gitEmail?.trim() || actor?.email || actor?.noreplyEmail;
|
|
224
|
-
// ---- 4. Store secrets (files) and references (config) ---------------
|
|
225
|
-
const claudeRef = claude
|
|
226
|
-
? (claude.ref ??
|
|
227
|
-
this.service.storeSecretFile(linearUserId, claudeKind === "oauthToken"
|
|
228
|
-
? CLAUDE_OAUTH_TOKEN_FILE
|
|
229
|
-
: CLAUDE_API_KEY_FILE, claude.secret))
|
|
230
|
-
: undefined;
|
|
231
|
-
const githubRef = this.service.storeGitHubToken(linearUserId, github.secret, github.ref);
|
|
232
|
-
const entry = {
|
|
233
|
-
displayName,
|
|
234
|
-
...(linearEmail ? { email: linearEmail } : {}),
|
|
235
|
-
...(claudeRef
|
|
236
|
-
? {
|
|
237
|
-
claude: claudeKind === "oauthToken"
|
|
238
|
-
? { oauthToken: claudeRef }
|
|
239
|
-
: { apiKey: claudeRef },
|
|
240
|
-
}
|
|
241
|
-
: {}),
|
|
242
|
-
github: {
|
|
243
|
-
token: githubRef,
|
|
244
|
-
...(actor ? { login: actor.login } : {}),
|
|
245
|
-
gitAuthorName,
|
|
246
|
-
...(gitAuthorEmail ? { gitAuthorEmail } : {}),
|
|
247
|
-
},
|
|
248
|
-
};
|
|
249
|
-
this.app.config.save(UserCredentialService.upsertUser(config, linearUserId, entry));
|
|
250
|
-
this.logDivider();
|
|
251
|
-
this.logSuccess(`Mapped ${displayName} (${linearUserId})`);
|
|
252
|
-
console.log(` Claude: ${claudeRef ? ("env" in claudeRef ? `env ${claudeRef.env}` : claudeRef.file) : "existing model authentication"}`);
|
|
253
|
-
console.log(` GitHub: github-tokens.json (personal entry)${actor ? ` (@${actor.login})` : ""}`);
|
|
254
|
-
console.log(` Git: ${gitAuthorName} <${gitAuthorEmail ?? "(host default)"}>`);
|
|
255
|
-
console.log("\nSessions this user triggers in Linear now run with these credentials. A running `cyrus` picks the change up automatically.");
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
export class ListUsersCommand extends BaseCommand {
|
|
259
|
-
async execute() {
|
|
260
|
-
const config = this.app.config.exists()
|
|
261
|
-
? this.app.config.load()
|
|
262
|
-
: undefined;
|
|
263
|
-
const users = config?.linearUsers ?? {};
|
|
264
|
-
const ids = Object.keys(users);
|
|
265
|
-
if (ids.length === 0) {
|
|
266
|
-
console.log("No Linear users mapped. Run `cyrus add-user` to add one.");
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
console.log(`\n${ids.length} mapped Linear user(s):\n`);
|
|
270
|
-
for (const id of ids) {
|
|
271
|
-
const info = UserCredentialService.inspectUser(id, users[id], this.app.cyrusHome);
|
|
272
|
-
console.log(`• ${info.displayName} (${id})`);
|
|
273
|
-
console.log(` Claude: ${info.claude.kind === "missing" ? "existing model authentication" : `${info.claude.kind} ${info.claude.ref} ${info.claude.ok ? `✅ #${info.claude.fingerprint}` : `❌ ${info.claude.error}`}`}`);
|
|
274
|
-
console.log(` GitHub: ${info.github.login ? `@${info.github.login} ` : ""}${info.github.ref} ${info.github.ok ? `✅ #${info.github.fingerprint}` : `❌ ${info.github.error}`}`);
|
|
275
|
-
const gh = users[id]?.github;
|
|
276
|
-
if (gh?.gitAuthorName || gh?.gitAuthorEmail) {
|
|
277
|
-
console.log(` Git: ${gh.gitAuthorName ?? ""} <${gh.gitAuthorEmail ?? ""}>`);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
const policy = resolvePrompterCredentialPolicy(config?.prompterCredentialPolicy);
|
|
281
|
-
console.log(`\nPolicy: unmappedPrompter=${policy.unmappedPrompter ?? "reject"} nonHumanTrigger=${policy.nonHumanTrigger ?? "reject"} externalPlatformSessions=${policy.externalPlatformSessions ?? "shared"} followUpByOtherUser=${policy.followUpByOtherUser ?? "pin"}`);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
export class CheckUsersCommand extends BaseCommand {
|
|
285
|
-
async execute(_args, options = {}) {
|
|
286
|
-
const config = this.app.config.exists()
|
|
287
|
-
? this.app.config.load()
|
|
288
|
-
: undefined;
|
|
289
|
-
const users = config?.linearUsers ?? {};
|
|
290
|
-
const ids = Object.keys(users);
|
|
291
|
-
if (ids.length === 0) {
|
|
292
|
-
console.log("No Linear users mapped.");
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
let failures = 0;
|
|
296
|
-
for (const id of ids) {
|
|
297
|
-
const entry = users[id];
|
|
298
|
-
const info = UserCredentialService.inspectUser(id, entry, this.app.cyrusHome);
|
|
299
|
-
console.log(`\n${info.displayName} (${id})`);
|
|
300
|
-
console.log(` Claude ${info.claude.kind === "missing" ? "existing model authentication" : info.claude.ok ? `✅ resolves (#${info.claude.fingerprint})` : `❌ ${info.claude.error}`}`);
|
|
301
|
-
console.log(` GitHub ${info.github.ok ? `✅ resolves (#${info.github.fingerprint})` : `❌ ${info.github.error}`}`);
|
|
302
|
-
if ((!info.claude.ok && info.claude.kind !== "missing") ||
|
|
303
|
-
!info.github.ok)
|
|
304
|
-
failures++;
|
|
305
|
-
if (!options.live)
|
|
306
|
-
continue;
|
|
307
|
-
// Live provider checks — real proof, not just "the reference resolves".
|
|
308
|
-
if (info.github.ok && entry.github?.token) {
|
|
309
|
-
const read = readGitHubCredentialRef(entry.github.token, id, this.app.cyrusHome);
|
|
310
|
-
const secret = "value" in read ? read.value : undefined;
|
|
311
|
-
try {
|
|
312
|
-
const actor = await UserCredentialService.verifyGitHubToken(secret ?? "");
|
|
313
|
-
const drift = entry.github.login && entry.github.login !== actor.login;
|
|
314
|
-
console.log(` GitHub API ✅ token acts as @${actor.login}${drift ? ` ⚠️ config says @${entry.github.login}` : ""}`);
|
|
315
|
-
if (drift)
|
|
316
|
-
failures++;
|
|
317
|
-
}
|
|
318
|
-
catch (error) {
|
|
319
|
-
console.log(` GitHub API ❌ ${error.message}`);
|
|
320
|
-
failures++;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
if (info.claude.ok) {
|
|
324
|
-
const claudeKind = info.claude.kind;
|
|
325
|
-
const ref = entry.claude?.oauthToken ?? entry.claude?.apiKey;
|
|
326
|
-
const secret = ref ? UserCredentialService.readSecret(ref) : undefined;
|
|
327
|
-
if (secret) {
|
|
328
|
-
const check = await verifyClaudeCredentialLive(this.app.cyrusHome, claudeKind, secret);
|
|
329
|
-
console.log(check.ok
|
|
330
|
-
? ` Claude API ✅ one-turn round trip succeeded${check.model ? ` (${check.model})` : ""}`
|
|
331
|
-
: ` Claude API ❌ ${check.error}`);
|
|
332
|
-
if (!check.ok)
|
|
333
|
-
failures++;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
console.log();
|
|
338
|
-
if (failures > 0) {
|
|
339
|
-
this.exitWithError(`${failures} problem(s) found. Fix with \`cyrus add-user --force\`.`);
|
|
340
|
-
}
|
|
341
|
-
this.logSuccess("All mapped users resolve.");
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
export class RemoveUserCommand extends BaseCommand {
|
|
345
|
-
service = new UserCredentialService(this.app.cyrusHome);
|
|
346
|
-
async execute(args) {
|
|
347
|
-
const target = args[0]?.trim();
|
|
348
|
-
if (!target) {
|
|
349
|
-
this.exitWithError("Usage: cyrus remove-user <linear-user-id | email>");
|
|
350
|
-
}
|
|
351
|
-
if (!this.app.config.exists()) {
|
|
352
|
-
this.exitWithError("No edge configuration found.");
|
|
353
|
-
}
|
|
354
|
-
const config = this.app.config.load();
|
|
355
|
-
const users = config.linearUsers ?? {};
|
|
356
|
-
const id = target in users
|
|
357
|
-
? target
|
|
358
|
-
: Object.keys(users).find((key) => users[key]?.email?.toLowerCase() === target.toLowerCase());
|
|
359
|
-
if (!id) {
|
|
360
|
-
this.exitWithError(`No mapped user matches "${target}". See \`cyrus list-users\`.`);
|
|
361
|
-
}
|
|
362
|
-
const entry = users[id];
|
|
363
|
-
const { config: next } = UserCredentialService.removeUser(config, id);
|
|
364
|
-
// Revoke first: if config persistence fails, the old mapping still refuses.
|
|
365
|
-
const removedFiles = this.service.removeStoredSecrets(id);
|
|
366
|
-
this.app.config.save(next);
|
|
367
|
-
this.logSuccess(`Removed ${entry.displayName ?? id} (${id})${removedFiles ? " and deleted the stored secret files" : ""}.`);
|
|
368
|
-
const envRefs = [
|
|
369
|
-
entry.claude?.oauthToken,
|
|
370
|
-
entry.claude?.apiKey,
|
|
371
|
-
entry.github?.token,
|
|
372
|
-
]
|
|
373
|
-
.filter((ref) => !!ref && "env" in ref)
|
|
374
|
-
.map((ref) => ref.env);
|
|
375
|
-
if (envRefs.length > 0) {
|
|
376
|
-
console.log(`Note: the environment variable(s) ${envRefs.join(", ")} still hold the secret(s); remove them from ~/.cyrus/.env yourself.`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
//# sourceMappingURL=UserCommands.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"UserCommands.js","sourceRoot":"","sources":["../../../src/commands/UserCommands.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UserCredentialService — provisioning of per-Linear-user execution
|
|
3
|
-
* credentials for multi-user self-hosted Cyrus (CYPACK-1502).
|
|
4
|
-
*
|
|
5
|
-
* Responsibilities:
|
|
6
|
-
* - keep personal GitHub entries in the shared github-tokens.json store
|
|
7
|
-
* - keep Claude secrets separate in user-credentials/ (0700 dir, 0600 files)
|
|
8
|
-
* - write ONLY credential references into config.json
|
|
9
|
-
* - verify the GitHub token against the GitHub API (actor login/name/email)
|
|
10
|
-
* - resolve a Linear user by email or ID through the workspace token
|
|
11
|
-
* - optionally run a real Claude round-trip with the user's credential
|
|
12
|
-
*
|
|
13
|
-
* Secrets are never logged or returned in error messages; only 8-char
|
|
14
|
-
* SHA-256 fingerprints are surfaced so operators can tell credentials apart.
|
|
15
|
-
*/
|
|
16
|
-
import { type CredentialRef, type EdgeConfig, type GitHubCredentialRef, type LinearUserConfig } from "cyrus-core";
|
|
17
|
-
/** Directory (under cyrusHome) holding one sub-directory per Linear user. */
|
|
18
|
-
export declare const USER_CREDENTIALS_DIR = "user-credentials";
|
|
19
|
-
export declare const CLAUDE_OAUTH_TOKEN_FILE = "claude-oauth-token";
|
|
20
|
-
export declare const CLAUDE_API_KEY_FILE = "claude-api-key";
|
|
21
|
-
export type ClaudeCredentialKind = "oauthToken" | "apiKey";
|
|
22
|
-
/**
|
|
23
|
-
* Classify a Claude credential by its well-known prefix.
|
|
24
|
-
* - `sk-ant-oat01-…` is a Claude Code OAuth token (`claude setup-token`)
|
|
25
|
-
* - `sk-ant-api03-…` (and other `sk-ant-api…`) is a console API key
|
|
26
|
-
* Anything else is unknown and the caller must ask.
|
|
27
|
-
*/
|
|
28
|
-
export declare function detectClaudeCredentialKind(secret: string): ClaudeCredentialKind | undefined;
|
|
29
|
-
export interface GitHubActor {
|
|
30
|
-
login: string;
|
|
31
|
-
id: number;
|
|
32
|
-
name: string | null;
|
|
33
|
-
email: string | null;
|
|
34
|
-
/** `<id>+<login>@users.noreply.github.com` — the address GitHub links to the account. */
|
|
35
|
-
noreplyEmail: string;
|
|
36
|
-
/** Token type hint from the response headers (fine-grained tokens have no scopes header). */
|
|
37
|
-
tokenScopes: string | null;
|
|
38
|
-
}
|
|
39
|
-
export interface LinearUserLookup {
|
|
40
|
-
id: string;
|
|
41
|
-
name: string;
|
|
42
|
-
displayName: string;
|
|
43
|
-
email: string;
|
|
44
|
-
active: boolean;
|
|
45
|
-
}
|
|
46
|
-
export declare class UserCredentialService {
|
|
47
|
-
private readonly cyrusHome;
|
|
48
|
-
constructor(cyrusHome: string);
|
|
49
|
-
userDir(linearUserId: string): string;
|
|
50
|
-
/**
|
|
51
|
-
* Write a secret to `<userDir>/<fileName>` with owner-only permissions and
|
|
52
|
-
* return a `{ file }` reference for config.json.
|
|
53
|
-
*/
|
|
54
|
-
storeSecretFile(linearUserId: string, fileName: string, secret: string): CredentialRef;
|
|
55
|
-
/** PATs share the installation-token store; Claude stays in separate files. */
|
|
56
|
-
storeGitHubToken(linearUserId: string, secret: string, envRef?: {
|
|
57
|
-
env: string;
|
|
58
|
-
}): GitHubCredentialRef;
|
|
59
|
-
/** Remove every stored secret for a user (no-op when nothing is stored). */
|
|
60
|
-
removeStoredSecrets(linearUserId: string): boolean;
|
|
61
|
-
/**
|
|
62
|
-
* Merge a user entry into the config's `linearUsers` map. Returns the new
|
|
63
|
-
* config object (caller persists it).
|
|
64
|
-
*/
|
|
65
|
-
static upsertUser(config: EdgeConfig, linearUserId: string, entry: LinearUserConfig): EdgeConfig;
|
|
66
|
-
static removeUser(config: EdgeConfig, linearUserId: string): {
|
|
67
|
-
config: EdgeConfig;
|
|
68
|
-
removed: boolean;
|
|
69
|
-
};
|
|
70
|
-
/**
|
|
71
|
-
* Verify a GitHub token by asking GitHub who it belongs to. Works for
|
|
72
|
-
* fine-grained and classic PATs. Throws a secret-free error on failure.
|
|
73
|
-
*/
|
|
74
|
-
static verifyGitHubToken(token: string, fetchImpl?: typeof fetch): Promise<GitHubActor>;
|
|
75
|
-
/**
|
|
76
|
-
* Resolve a Linear user by ID or email using the workspace's OAuth token.
|
|
77
|
-
* Throws a secret-free error when no single active match exists.
|
|
78
|
-
*/
|
|
79
|
-
static lookupLinearUser(linearToken: string, query: {
|
|
80
|
-
id?: string;
|
|
81
|
-
email?: string;
|
|
82
|
-
}, fetchImpl?: typeof fetch): Promise<LinearUserLookup>;
|
|
83
|
-
/**
|
|
84
|
-
* Secret-free status of one mapped user: whether each reference resolves
|
|
85
|
-
* and a fingerprint of the resolved value (for telling users apart).
|
|
86
|
-
*/
|
|
87
|
-
static inspectUser(linearUserId: string, entry: LinearUserConfig, cyrusHome: string): {
|
|
88
|
-
linearUserId: string;
|
|
89
|
-
displayName: string;
|
|
90
|
-
claude: {
|
|
91
|
-
kind: ClaudeCredentialKind;
|
|
92
|
-
ref: string;
|
|
93
|
-
ok: true;
|
|
94
|
-
fingerprint: string;
|
|
95
|
-
} | {
|
|
96
|
-
kind: ClaudeCredentialKind | "missing";
|
|
97
|
-
ref: string;
|
|
98
|
-
ok: false;
|
|
99
|
-
error: string;
|
|
100
|
-
};
|
|
101
|
-
github: {
|
|
102
|
-
ref: string;
|
|
103
|
-
ok: true;
|
|
104
|
-
fingerprint: string;
|
|
105
|
-
login?: string;
|
|
106
|
-
} | {
|
|
107
|
-
ref: string;
|
|
108
|
-
ok: false;
|
|
109
|
-
error: string;
|
|
110
|
-
login?: string;
|
|
111
|
-
};
|
|
112
|
-
};
|
|
113
|
-
/** Read a stored secret back (for `check-users --live`). */
|
|
114
|
-
static readSecret(ref: CredentialRef): string | undefined;
|
|
115
|
-
/** Is a file ref inside this service's managed directory? */
|
|
116
|
-
isManagedFile(ref: CredentialRef): boolean;
|
|
117
|
-
/** Read a secret from a file path (used for --*-token-file inputs). */
|
|
118
|
-
static readSecretFromFile(path: string): string;
|
|
119
|
-
}
|
|
120
|
-
//# sourceMappingURL=UserCredentialService.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"UserCredentialService.d.ts","sourceRoot":"","sources":["../../../src/services/UserCredentialService.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UserCredentialService — provisioning of per-Linear-user execution
|
|
3
|
-
* credentials for multi-user self-hosted Cyrus (CYPACK-1502).
|
|
4
|
-
*
|
|
5
|
-
* Responsibilities:
|
|
6
|
-
* - keep personal GitHub entries in the shared github-tokens.json store
|
|
7
|
-
* - keep Claude secrets separate in user-credentials/ (0700 dir, 0600 files)
|
|
8
|
-
* - write ONLY credential references into config.json
|
|
9
|
-
* - verify the GitHub token against the GitHub API (actor login/name/email)
|
|
10
|
-
* - resolve a Linear user by email or ID through the workspace token
|
|
11
|
-
* - optionally run a real Claude round-trip with the user's credential
|
|
12
|
-
*
|
|
13
|
-
* Secrets are never logged or returned in error messages; only 8-char
|
|
14
|
-
* SHA-256 fingerprints are surfaced so operators can tell credentials apart.
|
|
15
|
-
*/
|
|
16
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
17
|
-
import { join } from "node:path";
|
|
18
|
-
import { credentialFingerprint, GitHubTokenStore, readCredentialRef, readGitHubCredentialRef, resolvePath, } from "cyrus-core";
|
|
19
|
-
/** Directory (under cyrusHome) holding one sub-directory per Linear user. */
|
|
20
|
-
export const USER_CREDENTIALS_DIR = "user-credentials";
|
|
21
|
-
export const CLAUDE_OAUTH_TOKEN_FILE = "claude-oauth-token";
|
|
22
|
-
export const CLAUDE_API_KEY_FILE = "claude-api-key";
|
|
23
|
-
/**
|
|
24
|
-
* Classify a Claude credential by its well-known prefix.
|
|
25
|
-
* - `sk-ant-oat01-…` is a Claude Code OAuth token (`claude setup-token`)
|
|
26
|
-
* - `sk-ant-api03-…` (and other `sk-ant-api…`) is a console API key
|
|
27
|
-
* Anything else is unknown and the caller must ask.
|
|
28
|
-
*/
|
|
29
|
-
export function detectClaudeCredentialKind(secret) {
|
|
30
|
-
if (/^sk-ant-oat\d*-/i.test(secret))
|
|
31
|
-
return "oauthToken";
|
|
32
|
-
if (/^sk-ant-api\d*-/i.test(secret))
|
|
33
|
-
return "apiKey";
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
|
-
export class UserCredentialService {
|
|
37
|
-
cyrusHome;
|
|
38
|
-
constructor(cyrusHome) {
|
|
39
|
-
this.cyrusHome = cyrusHome;
|
|
40
|
-
}
|
|
41
|
-
// ---------------------------------------------------------------------
|
|
42
|
-
// Storage
|
|
43
|
-
// ---------------------------------------------------------------------
|
|
44
|
-
userDir(linearUserId) {
|
|
45
|
-
return join(this.cyrusHome, USER_CREDENTIALS_DIR, sanitizeId(linearUserId));
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Write a secret to `<userDir>/<fileName>` with owner-only permissions and
|
|
49
|
-
* return a `{ file }` reference for config.json.
|
|
50
|
-
*/
|
|
51
|
-
storeSecretFile(linearUserId, fileName, secret) {
|
|
52
|
-
const dir = this.userDir(linearUserId);
|
|
53
|
-
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
54
|
-
chmodSync(dir, 0o700);
|
|
55
|
-
const path = join(dir, fileName);
|
|
56
|
-
writeFileSync(path, `${secret.trim()}\n`, { mode: 0o600 });
|
|
57
|
-
chmodSync(path, 0o600);
|
|
58
|
-
return { file: path };
|
|
59
|
-
}
|
|
60
|
-
/** PATs share the installation-token store; Claude stays in separate files. */
|
|
61
|
-
storeGitHubToken(linearUserId, secret, envRef) {
|
|
62
|
-
new GitHubTokenStore(this.cyrusHome).setPersonalToken(linearUserId, envRef ?? { token: secret.trim() });
|
|
63
|
-
return { store: "github-tokens" };
|
|
64
|
-
}
|
|
65
|
-
/** Remove every stored secret for a user (no-op when nothing is stored). */
|
|
66
|
-
removeStoredSecrets(linearUserId) {
|
|
67
|
-
new GitHubTokenStore(this.cyrusHome).removePersonalToken(linearUserId);
|
|
68
|
-
const dir = this.userDir(linearUserId);
|
|
69
|
-
if (!existsSync(dir))
|
|
70
|
-
return false;
|
|
71
|
-
rmSync(dir, { recursive: true, force: true });
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Merge a user entry into the config's `linearUsers` map. Returns the new
|
|
76
|
-
* config object (caller persists it).
|
|
77
|
-
*/
|
|
78
|
-
static upsertUser(config, linearUserId, entry) {
|
|
79
|
-
return {
|
|
80
|
-
...config,
|
|
81
|
-
linearUsers: { ...(config.linearUsers ?? {}), [linearUserId]: entry },
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
static removeUser(config, linearUserId) {
|
|
85
|
-
const users = { ...(config.linearUsers ?? {}) };
|
|
86
|
-
const removed = linearUserId in users;
|
|
87
|
-
delete users[linearUserId];
|
|
88
|
-
const next = { ...config, linearUsers: users };
|
|
89
|
-
if (Object.keys(users).length === 0) {
|
|
90
|
-
delete next.linearUsers;
|
|
91
|
-
}
|
|
92
|
-
return { config: next, removed };
|
|
93
|
-
}
|
|
94
|
-
// ---------------------------------------------------------------------
|
|
95
|
-
// Verification
|
|
96
|
-
// ---------------------------------------------------------------------
|
|
97
|
-
/**
|
|
98
|
-
* Verify a GitHub token by asking GitHub who it belongs to. Works for
|
|
99
|
-
* fine-grained and classic PATs. Throws a secret-free error on failure.
|
|
100
|
-
*/
|
|
101
|
-
static async verifyGitHubToken(token, fetchImpl = fetch) {
|
|
102
|
-
const response = await fetchImpl("https://api.github.com/user", {
|
|
103
|
-
headers: {
|
|
104
|
-
Authorization: `Bearer ${token}`,
|
|
105
|
-
Accept: "application/vnd.github+json",
|
|
106
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
107
|
-
"User-Agent": "cyrus-cli",
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
if (response.status === 401) {
|
|
111
|
-
throw new Error("GitHub rejected the token (401 Unauthorized). Check that it is a valid, unexpired personal access token.");
|
|
112
|
-
}
|
|
113
|
-
if (!response.ok) {
|
|
114
|
-
throw new Error(`GitHub token verification failed: HTTP ${response.status}`);
|
|
115
|
-
}
|
|
116
|
-
const body = (await response.json());
|
|
117
|
-
return {
|
|
118
|
-
login: body.login,
|
|
119
|
-
id: body.id,
|
|
120
|
-
name: body.name ?? null,
|
|
121
|
-
email: body.email ?? null,
|
|
122
|
-
noreplyEmail: `${body.id}+${body.login}@users.noreply.github.com`,
|
|
123
|
-
tokenScopes: response.headers.get("x-oauth-scopes"),
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Resolve a Linear user by ID or email using the workspace's OAuth token.
|
|
128
|
-
* Throws a secret-free error when no single active match exists.
|
|
129
|
-
*/
|
|
130
|
-
static async lookupLinearUser(linearToken, query, fetchImpl = fetch) {
|
|
131
|
-
const body = query.id
|
|
132
|
-
? {
|
|
133
|
-
query: "query($id: String!) { user(id: $id) { id name displayName email active } }",
|
|
134
|
-
variables: { id: query.id },
|
|
135
|
-
}
|
|
136
|
-
: {
|
|
137
|
-
query: "query($email: String!) { users(filter: { email: { eq: $email } }) { nodes { id name displayName email active } } }",
|
|
138
|
-
variables: { email: query.email },
|
|
139
|
-
};
|
|
140
|
-
const response = await fetchImpl("https://api.linear.app/graphql", {
|
|
141
|
-
method: "POST",
|
|
142
|
-
headers: {
|
|
143
|
-
"Content-Type": "application/json",
|
|
144
|
-
Authorization: linearToken,
|
|
145
|
-
},
|
|
146
|
-
body: JSON.stringify(body),
|
|
147
|
-
});
|
|
148
|
-
const json = (await response.json());
|
|
149
|
-
if (json.errors?.length) {
|
|
150
|
-
throw new Error(`Linear lookup failed: ${json.errors[0]?.message ?? "unknown error"}`);
|
|
151
|
-
}
|
|
152
|
-
const candidates = query.id
|
|
153
|
-
? json.data?.user
|
|
154
|
-
? [json.data.user]
|
|
155
|
-
: []
|
|
156
|
-
: (json.data?.users?.nodes ?? []);
|
|
157
|
-
if (candidates.length === 0) {
|
|
158
|
-
throw new Error(`No Linear user found for ${query.id ? `id ${query.id}` : `email ${query.email}`}`);
|
|
159
|
-
}
|
|
160
|
-
if (candidates.length > 1) {
|
|
161
|
-
throw new Error(`Linear returned ${candidates.length} users for ${query.email}; pass --linear-user-id instead`);
|
|
162
|
-
}
|
|
163
|
-
return candidates[0];
|
|
164
|
-
}
|
|
165
|
-
// ---------------------------------------------------------------------
|
|
166
|
-
// Inspection (list / check)
|
|
167
|
-
// ---------------------------------------------------------------------
|
|
168
|
-
/**
|
|
169
|
-
* Secret-free status of one mapped user: whether each reference resolves
|
|
170
|
-
* and a fingerprint of the resolved value (for telling users apart).
|
|
171
|
-
*/
|
|
172
|
-
static inspectUser(linearUserId, entry, cyrusHome) {
|
|
173
|
-
const claudeRef = entry.claude?.oauthToken ?? entry.claude?.apiKey;
|
|
174
|
-
const claudeKind = entry.claude
|
|
175
|
-
?.oauthToken
|
|
176
|
-
? "oauthToken"
|
|
177
|
-
: entry.claude?.apiKey
|
|
178
|
-
? "apiKey"
|
|
179
|
-
: "missing";
|
|
180
|
-
let claude;
|
|
181
|
-
if (!claudeRef || claudeKind === "missing") {
|
|
182
|
-
claude = {
|
|
183
|
-
kind: "missing",
|
|
184
|
-
ref: "—",
|
|
185
|
-
ok: false,
|
|
186
|
-
error: "no Claude credential reference",
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
const read = readCredentialRef(claudeRef);
|
|
191
|
-
claude =
|
|
192
|
-
"value" in read
|
|
193
|
-
? {
|
|
194
|
-
kind: claudeKind,
|
|
195
|
-
ref: describeRef(claudeRef),
|
|
196
|
-
ok: true,
|
|
197
|
-
fingerprint: credentialFingerprint(read.value),
|
|
198
|
-
}
|
|
199
|
-
: {
|
|
200
|
-
kind: claudeKind,
|
|
201
|
-
ref: describeRef(claudeRef),
|
|
202
|
-
ok: false,
|
|
203
|
-
error: read.error,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
let github;
|
|
207
|
-
if (!entry.github?.token) {
|
|
208
|
-
github = { ref: "—", ok: false, error: "no GitHub token reference" };
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
const read = readGitHubCredentialRef(entry.github.token, linearUserId, cyrusHome);
|
|
212
|
-
github =
|
|
213
|
-
"value" in read
|
|
214
|
-
? {
|
|
215
|
-
ref: describeRef(entry.github.token),
|
|
216
|
-
ok: true,
|
|
217
|
-
fingerprint: credentialFingerprint(read.value),
|
|
218
|
-
login: entry.github.login,
|
|
219
|
-
}
|
|
220
|
-
: {
|
|
221
|
-
ref: describeRef(entry.github.token),
|
|
222
|
-
ok: false,
|
|
223
|
-
error: read.error,
|
|
224
|
-
login: entry.github.login,
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
return {
|
|
228
|
-
linearUserId,
|
|
229
|
-
displayName: entry.displayName || linearUserId,
|
|
230
|
-
claude,
|
|
231
|
-
github,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
/** Read a stored secret back (for `check-users --live`). */
|
|
235
|
-
static readSecret(ref) {
|
|
236
|
-
const read = readCredentialRef(ref);
|
|
237
|
-
return "value" in read ? read.value : undefined;
|
|
238
|
-
}
|
|
239
|
-
/** Is a file ref inside this service's managed directory? */
|
|
240
|
-
isManagedFile(ref) {
|
|
241
|
-
if (!("file" in ref))
|
|
242
|
-
return false;
|
|
243
|
-
return resolvePath(ref.file).startsWith(join(this.cyrusHome, USER_CREDENTIALS_DIR));
|
|
244
|
-
}
|
|
245
|
-
/** Read a secret from a file path (used for --*-token-file inputs). */
|
|
246
|
-
static readSecretFromFile(path) {
|
|
247
|
-
const value = readFileSync(resolvePath(path), "utf-8").trim();
|
|
248
|
-
if (!value)
|
|
249
|
-
throw new Error(`${path} is empty`);
|
|
250
|
-
return value;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
function sanitizeId(id) {
|
|
254
|
-
return id.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
255
|
-
}
|
|
256
|
-
function describeRef(ref) {
|
|
257
|
-
if ("store" in ref)
|
|
258
|
-
return "github-tokens.json (personal entry)";
|
|
259
|
-
return "env" in ref ? `env:${ref.env}` : `file:${ref.file}`;
|
|
260
|
-
}
|
|
261
|
-
//# sourceMappingURL=UserCredentialService.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"UserCredentialService.js","sourceRoot":"","sources":["../../../src/services/UserCredentialService.ts"],"names":[],"mappings":"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"}
|