@gjermundgaraba/clankercreds 0.3.0 → 0.5.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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as Name, c as SecretKind, d as scopes, f as CurrentPrincipal, i as LoginKind, l as UpstreamUnavailable, n as Conflict, o as NeedsSignIn, p as ProviderUnavailable, r as Host, s as NotFound } from "./shared-DyTG-WFI.mjs";
|
|
2
2
|
import { Config, Console, Effect, Option, Redacted, Schema } from "effect";
|
|
3
3
|
import * as Action from "@gjermundgaraba/effect-actions/Action";
|
|
4
4
|
import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
|
|
@@ -12,8 +12,6 @@ import * as ActionCli from "@gjermundgaraba/effect-actions/ActionCli";
|
|
|
12
12
|
//#region ../../packages/api/src/admin.ts
|
|
13
13
|
/** Written by the service, always ISO. What an operator enters is decoded as a date instead. */
|
|
14
14
|
const Timestamp = Schema.String;
|
|
15
|
-
/** The kinds an operator stores a value for. A ChatGPT login only ever comes from a sign-in. */
|
|
16
|
-
const SecretKind = Schema.Literals(["gh", "claude"]);
|
|
17
15
|
/** A stored secret's state. The value itself is never read back out. */
|
|
18
16
|
const SecretStatus = Schema.Struct({
|
|
19
17
|
name: Name,
|
|
@@ -24,20 +22,21 @@ const SecretStatus = Schema.Struct({
|
|
|
24
22
|
createdAt: Timestamp,
|
|
25
23
|
updatedAt: Timestamp
|
|
26
24
|
});
|
|
27
|
-
/** A
|
|
28
|
-
const
|
|
25
|
+
/** A login's state. It exists from the first completed sign-in on. */
|
|
26
|
+
const LoginStatus = Schema.Struct({
|
|
29
27
|
name: Name,
|
|
30
|
-
kind:
|
|
28
|
+
kind: LoginKind,
|
|
31
29
|
needsSignIn: Schema.Boolean,
|
|
32
30
|
email: Schema.NullOr(Schema.String),
|
|
33
|
-
|
|
31
|
+
/** The ChatGPT account a ChatGPT login is for; null for every other kind. */
|
|
32
|
+
accountId: Schema.NullOr(Schema.String),
|
|
34
33
|
accessExpiresAt: Timestamp,
|
|
35
34
|
lastRefreshAt: Timestamp,
|
|
36
35
|
lastError: Schema.NullOr(Schema.String),
|
|
37
36
|
createdAt: Timestamp,
|
|
38
37
|
updatedAt: Timestamp
|
|
39
38
|
});
|
|
40
|
-
const CredentialStatus = Schema.Union([SecretStatus,
|
|
39
|
+
const CredentialStatus = Schema.Union([SecretStatus, LoginStatus]);
|
|
41
40
|
/** The clankerauth id of a machine key. */
|
|
42
41
|
const KeyId = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(256));
|
|
43
42
|
/**
|
|
@@ -65,16 +64,21 @@ const ClaudeSignIn = Schema.Struct({
|
|
|
65
64
|
* `authorizeUrl`. An approval that left out a scope the token needs ends it: start again.
|
|
66
65
|
*/
|
|
67
66
|
var SignInRefused = class extends Schema.TaggedError()("SignInRefused", { message: Schema.String }, { httpApiStatus: 422 }) {};
|
|
67
|
+
/**
|
|
68
|
+
* A login's sign-in: open `verificationUrl` and approve, entering `userCode` where the
|
|
69
|
+
* provider asks for one. Cursor's link carries everything and has no code.
|
|
70
|
+
*/
|
|
68
71
|
const SignInState = Schema.Struct({
|
|
69
72
|
signInId: Schema.String,
|
|
70
73
|
credential: Name,
|
|
74
|
+
kind: LoginKind,
|
|
71
75
|
status: Schema.Literals([
|
|
72
76
|
"pending",
|
|
73
77
|
"complete",
|
|
74
78
|
"failed",
|
|
75
79
|
"expired"
|
|
76
80
|
]),
|
|
77
|
-
userCode: Schema.String,
|
|
81
|
+
userCode: Schema.NullOr(Schema.String),
|
|
78
82
|
verificationUrl: Schema.String,
|
|
79
83
|
expiresAt: Timestamp,
|
|
80
84
|
message: Schema.optionalKey(Schema.String)
|
|
@@ -108,10 +112,10 @@ const AuditQuery = Schema.Struct({
|
|
|
108
112
|
clientId: Schema.optionalKey(Schema.String)
|
|
109
113
|
});
|
|
110
114
|
/**
|
|
111
|
-
* A read whose
|
|
115
|
+
* A read whose code or link signs an account in: exported so the server's authorization
|
|
112
116
|
* rule can name it.
|
|
113
117
|
*/
|
|
114
|
-
const
|
|
118
|
+
const GetSignIn = Action.make("getSignIn", {
|
|
115
119
|
readOnly: true,
|
|
116
120
|
caller: CurrentPrincipal,
|
|
117
121
|
description: "Read a sign-in. The service holds one in memory only, until the next sign-in starts after it expired; one it no longer holds is not found.",
|
|
@@ -124,17 +128,18 @@ const Admin = [
|
|
|
124
128
|
Action.make("listCredentials", {
|
|
125
129
|
readOnly: true,
|
|
126
130
|
caller: CurrentPrincipal,
|
|
127
|
-
description: "Every credential with its state: expiry, last refresh, last error and whether a
|
|
131
|
+
description: "Every credential with its state: expiry, last refresh, last error and whether a login needs a new sign-in. Values are never returned.",
|
|
128
132
|
success: Schema.Struct({ credentials: Schema.Array(CredentialStatus) })
|
|
129
133
|
}),
|
|
130
134
|
Action.make("putCredential", {
|
|
131
135
|
readOnly: false,
|
|
132
136
|
caller: CurrentPrincipal,
|
|
133
|
-
description: "Store a secret under a name, or replace its value and expiry. A `gh` token is for `host`, github.com unless given. A credential's kind and host never change; remove it and put it again instead.
|
|
137
|
+
description: "Store a secret under a name, or replace its value and expiry: a `gh` or `claude` token a machine places, or a `token`, any other service's API key, which a service such as clankerusage reads. A `gh` token is for `host`, github.com unless given. A credential's kind and host never change; remove it and put it again instead. Keys granted it receive it on their next sync.",
|
|
134
138
|
input: Schema.Struct({
|
|
135
139
|
name: Name,
|
|
136
140
|
kind: SecretKind,
|
|
137
|
-
|
|
141
|
+
/** Surrounding whitespace, such as a pasted line ending, is not part of a secret: a value carrying any is refused, never altered. */
|
|
142
|
+
value: Schema.Trimmed.check(Schema.isMinLength(1), Schema.isMaxLength(16384)),
|
|
138
143
|
host: Schema.optionalKey(Host),
|
|
139
144
|
/** Any date string JavaScript parses; a typo is refused, so alerting never loses it. Left out, it does not expire. */
|
|
140
145
|
expiresAt: Schema.optionalKey(Schema.DateFromString)
|
|
@@ -153,21 +158,24 @@ const Admin = [
|
|
|
153
158
|
success: Schema.Void,
|
|
154
159
|
error: [NotFound, Conflict]
|
|
155
160
|
}),
|
|
156
|
-
Action.make("
|
|
161
|
+
Action.make("startSignIn", {
|
|
157
162
|
readOnly: false,
|
|
158
163
|
caller: CurrentPrincipal,
|
|
159
|
-
description: "Start a
|
|
160
|
-
input: Schema.Struct({
|
|
164
|
+
description: "Start a sign-in for a ChatGPT, Grok or Cursor login; the credential is created, or its login replaced, when the sign-in completes. Open the returned verificationUrl and approve, entering userCode where one is given, then poll getSignIn.",
|
|
165
|
+
input: Schema.Struct({
|
|
166
|
+
name: Name,
|
|
167
|
+
kind: LoginKind
|
|
168
|
+
}),
|
|
161
169
|
success: SignInState,
|
|
162
170
|
error: [Conflict, UpstreamUnavailable]
|
|
163
171
|
}),
|
|
164
|
-
|
|
165
|
-
Action.make("
|
|
172
|
+
GetSignIn,
|
|
173
|
+
Action.make("refreshCredential", {
|
|
166
174
|
readOnly: false,
|
|
167
175
|
caller: CurrentPrincipal,
|
|
168
|
-
description: "Refresh one
|
|
176
|
+
description: "Refresh one login now instead of waiting for its schedule.",
|
|
169
177
|
input: Schema.Struct({ name: Name }),
|
|
170
|
-
success:
|
|
178
|
+
success: LoginStatus,
|
|
171
179
|
error: [
|
|
172
180
|
NotFound,
|
|
173
181
|
Conflict,
|
|
@@ -236,7 +244,7 @@ const AdminHttp = ActionHttp.make(Admin, {
|
|
|
236
244
|
});
|
|
237
245
|
//#endregion
|
|
238
246
|
//#region package.json
|
|
239
|
-
var version = "0.
|
|
247
|
+
var version = "0.5.0";
|
|
240
248
|
//#endregion
|
|
241
249
|
//#region src/admin.ts
|
|
242
250
|
/** A refusal of this command's own, printed as its message on stderr, exiting 1. */
|
|
@@ -287,11 +295,11 @@ const operator = Effect.gen(function* () {
|
|
|
287
295
|
});
|
|
288
296
|
/**
|
|
289
297
|
* The secret's value, from stdin: a flag would leave it in the process list and the shell's
|
|
290
|
-
* history.
|
|
298
|
+
* history. Surrounding whitespace, such as the line ending `op read` adds, is not part of it.
|
|
291
299
|
*/
|
|
292
300
|
const valueFromStdin = Effect.gen(function* () {
|
|
293
301
|
if (process.stdin.isTTY) return yield* refused("Pipe the value in, such as `op read 'op://…' | clankercreds admin put-credential …`; it is never taken from a flag or the terminal.");
|
|
294
|
-
const value = (yield* Effect.promise(() => text(process.stdin))).
|
|
302
|
+
const value = (yield* Effect.promise(() => text(process.stdin))).trim();
|
|
295
303
|
if (value === "") return yield* refused("The value on stdin is empty.");
|
|
296
304
|
return value;
|
|
297
305
|
});
|
|
@@ -299,7 +307,7 @@ const json = (value) => Console.log(JSON.stringify(value, null, 2));
|
|
|
299
307
|
/** `putCredential`, its value read from stdin and every other field a flag. */
|
|
300
308
|
const putCredential = Command.make("put-credential", {
|
|
301
309
|
name: Flag.String("name").pipe(Flag.withDescription("The credential's name")),
|
|
302
|
-
kind: Flag.Literals("kind", SecretKind.literals).pipe(Flag.withDescription("
|
|
310
|
+
kind: Flag.Literals("kind", SecretKind.literals).pipe(Flag.withDescription("What the secret is: a gh or claude token a machine places, or a token, any other service's API key")),
|
|
303
311
|
host: Flag.String("host").pipe(Flag.withDescription("A gh token's host; github.com when left out"), Flag.optional),
|
|
304
312
|
expiresAt: Flag.String("expires-at").pipe(Flag.withDescription("When the secret expires, any date string; it never does when left out"), Flag.optional)
|
|
305
313
|
}, ({ name, kind, host, expiresAt }) => Effect.gen(function* () {
|
package/dist/main.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as Name, f as CurrentPrincipal, l as UpstreamUnavailable, o as NeedsSignIn, p as ProviderUnavailable, s as NotFound, t as ClientId, u as claimsOf } from "./shared-DyTG-WFI.mjs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { Effect, Option, Predicate, Schema } from "effect";
|
|
@@ -11,8 +11,16 @@ import { randomBytes } from "node:crypto";
|
|
|
11
11
|
import { HttpClient, HttpClientRequest } from "effect/http";
|
|
12
12
|
import { YAMLMap, isMap, parseDocument } from "yaml";
|
|
13
13
|
//#region ../../packages/api/src/credentials.ts
|
|
14
|
-
/**
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The credential an entry was delivered from, as it was asked for by name, and when what it
|
|
16
|
+
* carries expires, as ISO: a secret's as the operator entered it, a login's access token's
|
|
17
|
+
* own. Null never expires. A login delivered past it could not be renewed in time, as when its
|
|
18
|
+
* provider is away: sync again later. Whether it needs a new sign-in is the operator's to see.
|
|
19
|
+
*/
|
|
20
|
+
const fromCredential = {
|
|
21
|
+
credential: Name,
|
|
22
|
+
expiresAt: Schema.NullOr(Schema.String)
|
|
23
|
+
};
|
|
16
24
|
const GhCredential = Schema.Struct({
|
|
17
25
|
type: Schema.Literal("gh"),
|
|
18
26
|
...fromCredential,
|
|
@@ -48,6 +56,17 @@ const PiCredential = Schema.Struct({
|
|
|
48
56
|
accessToken: Schema.String
|
|
49
57
|
});
|
|
50
58
|
/**
|
|
59
|
+
* A credential no machine places: an API key, or a Grok or Cursor login's access token, for
|
|
60
|
+
* a service such as clankerusage. `kind` is the credential's own, and any string, so a kind
|
|
61
|
+
* newer than the caller's contract still decodes. The client skips one a config names.
|
|
62
|
+
*/
|
|
63
|
+
const TokenCredential = Schema.Struct({
|
|
64
|
+
type: Schema.Literal("token"),
|
|
65
|
+
...fromCredential,
|
|
66
|
+
kind: Schema.String,
|
|
67
|
+
token: Schema.String
|
|
68
|
+
});
|
|
69
|
+
/**
|
|
51
70
|
* The service sends typed credentials, never file contents or paths, so it cannot write
|
|
52
71
|
* anything on a machine but what the client knows how to place. The cost is that a new
|
|
53
72
|
* tool, or a placement fix, needs a client release, which new machines install. Each entry
|
|
@@ -64,7 +83,8 @@ const TypedCredential = Schema.Union([
|
|
|
64
83
|
GhCredential,
|
|
65
84
|
ClaudeCredential,
|
|
66
85
|
CodexCredential,
|
|
67
|
-
PiCredential
|
|
86
|
+
PiCredential,
|
|
87
|
+
TokenCredential
|
|
68
88
|
]);
|
|
69
89
|
/** The types this build knows, derived from the union so there is one list. */
|
|
70
90
|
const knownTypes = TypedCredential.members.map((member) => member.fields.type.literal);
|
|
@@ -75,39 +95,57 @@ const isKnownType = (type) => knownTypes.some((known) => known === type);
|
|
|
75
95
|
* schema must not land here, or a broken credential would be placed as an empty one.
|
|
76
96
|
*/
|
|
77
97
|
const UnknownCredential = Schema.Struct({ type: Schema.String.check(Schema.makeFilter((type) => !isKnownType(type), { message: "a known credential type must match its schema" })) });
|
|
78
|
-
/** Everything the
|
|
98
|
+
/** Everything the caller asked for, sent in full on every sync. */
|
|
79
99
|
const Delivery = Schema.Struct({ credentials: Schema.Array(Schema.Union([TypedCredential, UnknownCredential])) });
|
|
80
100
|
//#endregion
|
|
81
101
|
//#region ../../packages/api/src/machine.ts
|
|
82
102
|
/**
|
|
103
|
+
* A credential the calling key may read, without its value. `kind` is any string, so a kind
|
|
104
|
+
* newer than the caller's contract does not fail the whole list.
|
|
105
|
+
*/
|
|
106
|
+
const Grant = Schema.Struct({
|
|
107
|
+
name: Name,
|
|
108
|
+
kind: Schema.String
|
|
109
|
+
});
|
|
110
|
+
/**
|
|
83
111
|
* What a machine key can do: read the credentials it is granted, by name, and nothing else.
|
|
84
|
-
* The
|
|
112
|
+
* The caller names what it wants; the grants on the server decide whether it may. A machine
|
|
113
|
+
* is one caller; a service such as clankerusage, holding a key of its own, is another.
|
|
85
114
|
*/
|
|
86
|
-
const Machine = [
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
115
|
+
const Machine = [
|
|
116
|
+
Action.make("sync", {
|
|
117
|
+
readOnly: false,
|
|
118
|
+
caller: CurrentPrincipal,
|
|
119
|
+
description: "Fetch the named credentials as typed entries, each with its expiry; a login near expiry is refreshed first. Refused whole when the calling key is not granted any one of them.",
|
|
120
|
+
input: Schema.Struct({
|
|
121
|
+
clientId: ClientId,
|
|
122
|
+
credentials: Schema.Array(Name)
|
|
123
|
+
}),
|
|
124
|
+
success: Delivery,
|
|
125
|
+
error: NotFound
|
|
93
126
|
}),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
127
|
+
Action.make("codexLogin", {
|
|
128
|
+
readOnly: false,
|
|
129
|
+
caller: CurrentPrincipal,
|
|
130
|
+
description: "The current login of a ChatGPT credential the calling key is granted, as the codex type carries it, refreshed first when it has expired.",
|
|
131
|
+
input: Schema.Struct({
|
|
132
|
+
clientId: ClientId,
|
|
133
|
+
credential: Name
|
|
134
|
+
}),
|
|
135
|
+
success: CodexLogin,
|
|
136
|
+
error: [
|
|
137
|
+
NotFound,
|
|
138
|
+
NeedsSignIn,
|
|
139
|
+
UpstreamUnavailable
|
|
140
|
+
]
|
|
103
141
|
}),
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
142
|
+
Action.make("listGrants", {
|
|
143
|
+
readOnly: true,
|
|
144
|
+
caller: CurrentPrincipal,
|
|
145
|
+
description: "The names and kinds of the credentials the calling key is granted, so a caller can offer them to pick from. Values are never returned.",
|
|
146
|
+
success: Schema.Struct({ credentials: Schema.Array(Grant) })
|
|
147
|
+
})
|
|
148
|
+
];
|
|
111
149
|
/**
|
|
112
150
|
* Codex treats a 401 as permanent and stops retrying; every other failure is transient.
|
|
113
151
|
* The codes are the ones Codex classifies: `refresh_token_invalidated` reads as revoked.
|
|
@@ -582,6 +620,9 @@ const placements = {
|
|
|
582
620
|
remove: (config) => removePi(config.home)
|
|
583
621
|
}
|
|
584
622
|
};
|
|
623
|
+
/** What this client places; anything else delivered, a token or a type it does not know, is skipped. */
|
|
624
|
+
const isPlaceable = (credential) => Object.hasOwn(placements, credential.type);
|
|
625
|
+
const placeableTypes = knownTypes.filter((type) => Object.hasOwn(placements, type));
|
|
585
626
|
/** What the delivery carries of one type is placed; a type it does not carry is undone. */
|
|
586
627
|
const settle = (config, identity, known, type) => {
|
|
587
628
|
const credentials = known.filter((credential) => credential.type === type);
|
|
@@ -602,16 +643,17 @@ const slot = (credential) => {
|
|
|
602
643
|
}
|
|
603
644
|
};
|
|
604
645
|
/**
|
|
605
|
-
* Two credentials for one slot would leave the machine holding whichever came last
|
|
646
|
+
* Two credentials for one slot would leave the machine holding whichever came last: the
|
|
606
647
|
* whole sync is refused before anything is written.
|
|
607
648
|
*/
|
|
608
|
-
const
|
|
649
|
+
const oneEach = (placed) => {
|
|
609
650
|
const claimed = /* @__PURE__ */ new Map();
|
|
610
|
-
for (const credential of
|
|
651
|
+
for (const credential of placed) {
|
|
611
652
|
const other = claimed.get(slot(credential));
|
|
612
653
|
if (other !== void 0 && other !== credential.credential) throw new Error(`${other} and ${credential.credential} are both ${slot(credential)}; a machine holds one, so config.json must name only one of them`);
|
|
613
654
|
claimed.set(slot(credential), credential.credential);
|
|
614
655
|
}
|
|
656
|
+
return placed;
|
|
615
657
|
};
|
|
616
658
|
/**
|
|
617
659
|
* Idempotent: every credential the config names is fetched and placed every time, and every
|
|
@@ -626,15 +668,14 @@ const sync = async (config) => {
|
|
|
626
668
|
key: config.key
|
|
627
669
|
};
|
|
628
670
|
const delivery = await fetchDelivery(config, identity.clientId);
|
|
629
|
-
const known = delivery.credentials.filter(
|
|
630
|
-
refuseShared(known);
|
|
671
|
+
const known = oneEach(delivery.credentials.filter(isPlaceable));
|
|
631
672
|
const report = {
|
|
632
673
|
placed: new Array(),
|
|
633
674
|
removed: new Array(),
|
|
634
|
-
skipped: delivery.credentials.flatMap((
|
|
675
|
+
skipped: delivery.credentials.flatMap((credential) => isPlaceable(credential) ? [] : [credential.type]),
|
|
635
676
|
failed: new Array()
|
|
636
677
|
};
|
|
637
|
-
for (const type of
|
|
678
|
+
for (const type of placeableTypes) try {
|
|
638
679
|
const outcome = settle(config, identity, known, type);
|
|
639
680
|
if (outcome !== void 0) report[outcome].push(type);
|
|
640
681
|
} catch (error) {
|
|
@@ -684,7 +725,7 @@ const usage = `Usage: clankercreds <command>
|
|
|
684
725
|
`;
|
|
685
726
|
const runSync = async () => {
|
|
686
727
|
const report = await sync(loadConfig());
|
|
687
|
-
for (const type of report.skipped) console.error(`warning: skipped
|
|
728
|
+
for (const type of report.skipped) console.error(`warning: skipped credential type "${type}", which this client does not place; if it is new, update clankercreds`);
|
|
688
729
|
for (const type of report.placed) console.log(`placed ${type}`);
|
|
689
730
|
for (const type of report.removed) console.log(`removed ${type}`);
|
|
690
731
|
for (const { type, message } of report.failed) console.error(`error: ${type}: ${message}`);
|
|
@@ -704,7 +745,7 @@ const commandFor = (name) => {
|
|
|
704
745
|
};
|
|
705
746
|
const main = async () => {
|
|
706
747
|
if (process.argv[2] === "admin") {
|
|
707
|
-
(await import("./admin-
|
|
748
|
+
(await import("./admin-BcOFNDUc.mjs")).run(process.argv.slice(2));
|
|
708
749
|
return;
|
|
709
750
|
}
|
|
710
751
|
const command = commandFor(process.argv[2]);
|
|
@@ -16,7 +16,7 @@ var ProviderUnavailable = class extends Schema.TaggedError()("ProviderUnavailabl
|
|
|
16
16
|
Data.TaggedError("InsufficientScope");
|
|
17
17
|
Schema.TaggedError()("ConfigurationError", { message: Schema.String });
|
|
18
18
|
//#endregion
|
|
19
|
-
//#region ../../node_modules/.pnpm/@gjermundgaraba+clankerauth-sdk@0.
|
|
19
|
+
//#region ../../node_modules/.pnpm/@gjermundgaraba+clankerauth-sdk@0.16.0_@gjermundgaraba+effect-actions@0.11.0_effect@4.0.2__effect@4.0.2/node_modules/@gjermundgaraba/clankerauth-sdk/dist/session.js
|
|
20
20
|
/**
|
|
21
21
|
* The identity a browser page needs, without any verification code. This entry point is
|
|
22
22
|
* browser-safe: it declares the identity a protected contract states, the `whoami` contract
|
|
@@ -62,9 +62,9 @@ const scopes = {
|
|
|
62
62
|
var NotFound = class extends Schema.TaggedError()("NotFound", { message: Schema.String }, { httpApiStatus: 404 }) {};
|
|
63
63
|
/** The change collides with what is stored: a taken name, or a credential still in use. */
|
|
64
64
|
var Conflict = class extends Schema.TaggedError()("Conflict", { message: Schema.String }, { httpApiStatus: 409 }) {};
|
|
65
|
-
/** The
|
|
65
|
+
/** The login is dead, or was never completed. Only a new sign-in revives it. */
|
|
66
66
|
var NeedsSignIn = class extends Schema.TaggedError()("NeedsSignIn", { message: Schema.String }, { httpApiStatus: 409 }) {};
|
|
67
|
-
/**
|
|
67
|
+
/** The provider could not be reached and no valid token is held. Waiting helps; retrying now does not. */
|
|
68
68
|
var UpstreamUnavailable = class extends Schema.TaggedError()("UpstreamUnavailable", { message: Schema.String }, { httpApiStatus: 503 }) {};
|
|
69
69
|
/**
|
|
70
70
|
* Credential names: lowercase labels, because they are typed by hand and are
|
|
@@ -72,6 +72,25 @@ var UpstreamUnavailable = class extends Schema.TaggedError()("UpstreamUnavailabl
|
|
|
72
72
|
* and `github-main` be two credentials.
|
|
73
73
|
*/
|
|
74
74
|
const Name = Schema.String.check(Schema.isPattern(/^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/));
|
|
75
|
+
/**
|
|
76
|
+
* What an operator stores a value for: a GitHub token, a Claude token, or `token`, any other
|
|
77
|
+
* service's API key, which a service such as clankerusage reads and no machine places.
|
|
78
|
+
*/
|
|
79
|
+
const SecretKind = Schema.Literals([
|
|
80
|
+
"gh",
|
|
81
|
+
"claude",
|
|
82
|
+
"token"
|
|
83
|
+
]);
|
|
84
|
+
/**
|
|
85
|
+
* What a sign-in creates and the service keeps refreshed. The refresh token never leaves
|
|
86
|
+
* the service; only access tokens are delivered.
|
|
87
|
+
*/
|
|
88
|
+
const LoginKind = Schema.Literals([
|
|
89
|
+
"chatgpt",
|
|
90
|
+
"grok",
|
|
91
|
+
"cursor"
|
|
92
|
+
]);
|
|
93
|
+
Schema.Literals([...SecretKind.literals, ...LoginKind.literals]);
|
|
75
94
|
/** A GitHub host, lowercase: it is written into `gh`'s and git's configuration as is. */
|
|
76
95
|
const Host = Schema.String.check(Schema.isPattern(/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*$/), Schema.isMaxLength(253));
|
|
77
96
|
/**
|
|
@@ -85,7 +104,7 @@ const Claims = Schema.Struct({
|
|
|
85
104
|
"https://api.openai.com/auth": Schema.optionalKey(Schema.Struct({ chatgpt_account_id: Schema.optionalKey(Schema.String) }))
|
|
86
105
|
});
|
|
87
106
|
/**
|
|
88
|
-
* The claims of
|
|
107
|
+
* The claims of a JWT, unverified: they are read for their timing, email and, in OpenAI's,
|
|
89
108
|
* account, never trusted for anything. Anything unreadable is simply no claims.
|
|
90
109
|
*/
|
|
91
110
|
const claimsOf = (jwt) => {
|
|
@@ -105,4 +124,4 @@ const claimsOf = (jwt) => {
|
|
|
105
124
|
});
|
|
106
125
|
};
|
|
107
126
|
//#endregion
|
|
108
|
-
export {
|
|
127
|
+
export { Name as a, SecretKind as c, scopes as d, CurrentPrincipal as f, LoginKind as i, UpstreamUnavailable as l, Conflict as n, NeedsSignIn as o, ProviderUnavailable as p, Host as r, NotFound as s, ClientId as t, claimsOf as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjermundgaraba/clankercreds",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "The clankercreds command: a machine client that fetches the credentials its config names and places each where its tool expects it, and the operator's admin commands.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -20,14 +20,15 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@effect/platform-node": "4.0.2",
|
|
23
|
+
"@effect/platform-node-shared": "4.0.2",
|
|
23
24
|
"@gjermundgaraba/effect-actions": "0.11.0",
|
|
24
25
|
"effect": "4.0.2",
|
|
25
26
|
"yaml": "2.9.1"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
|
-
"@
|
|
29
|
-
"@gjermundgaraba/
|
|
30
|
-
"@types/node": "26.6.
|
|
29
|
+
"@gjermundgaraba/clankerauth-sdk": "0.16.0",
|
|
30
|
+
"@gjermundgaraba/clankercreds-api": "0.5.0",
|
|
31
|
+
"@types/node": "26.6.5",
|
|
31
32
|
"typescript": "7.0.2",
|
|
32
33
|
"vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
|
|
33
34
|
"vite-plus": "1.1.0"
|