@gjermundgaraba/clankercreds 0.3.0 → 0.4.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 LoginKind, c as NotFound, f as scopes, i as Host, l as SecretKind, m as ProviderUnavailable, n as Conflict, o as Name, p as CurrentPrincipal, s as NeedsSignIn, u as UpstreamUnavailable } from "./shared-C56uw1xx.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,13 +128,13 @@ 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,
|
|
@@ -153,21 +157,24 @@ const Admin = [
|
|
|
153
157
|
success: Schema.Void,
|
|
154
158
|
error: [NotFound, Conflict]
|
|
155
159
|
}),
|
|
156
|
-
Action.make("
|
|
160
|
+
Action.make("startSignIn", {
|
|
157
161
|
readOnly: false,
|
|
158
162
|
caller: CurrentPrincipal,
|
|
159
|
-
description: "Start a
|
|
160
|
-
input: Schema.Struct({
|
|
163
|
+
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.",
|
|
164
|
+
input: Schema.Struct({
|
|
165
|
+
name: Name,
|
|
166
|
+
kind: LoginKind
|
|
167
|
+
}),
|
|
161
168
|
success: SignInState,
|
|
162
169
|
error: [Conflict, UpstreamUnavailable]
|
|
163
170
|
}),
|
|
164
|
-
|
|
165
|
-
Action.make("
|
|
171
|
+
GetSignIn,
|
|
172
|
+
Action.make("refreshCredential", {
|
|
166
173
|
readOnly: false,
|
|
167
174
|
caller: CurrentPrincipal,
|
|
168
|
-
description: "Refresh one
|
|
175
|
+
description: "Refresh one login now instead of waiting for its schedule.",
|
|
169
176
|
input: Schema.Struct({ name: Name }),
|
|
170
|
-
success:
|
|
177
|
+
success: LoginStatus,
|
|
171
178
|
error: [
|
|
172
179
|
NotFound,
|
|
173
180
|
Conflict,
|
|
@@ -236,7 +243,7 @@ const AdminHttp = ActionHttp.make(Admin, {
|
|
|
236
243
|
});
|
|
237
244
|
//#endregion
|
|
238
245
|
//#region package.json
|
|
239
|
-
var version = "0.
|
|
246
|
+
var version = "0.4.0";
|
|
240
247
|
//#endregion
|
|
241
248
|
//#region src/admin.ts
|
|
242
249
|
/** A refusal of this command's own, printed as its message on stderr, exiting 1. */
|
|
@@ -299,7 +306,7 @@ const json = (value) => Console.log(JSON.stringify(value, null, 2));
|
|
|
299
306
|
/** `putCredential`, its value read from stdin and every other field a flag. */
|
|
300
307
|
const putCredential = Command.make("put-credential", {
|
|
301
308
|
name: Flag.String("name").pipe(Flag.withDescription("The credential's name")),
|
|
302
|
-
kind: Flag.Literals("kind", SecretKind.literals).pipe(Flag.withDescription("
|
|
309
|
+
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
310
|
host: Flag.String("host").pipe(Flag.withDescription("A gh token's host; github.com when left out"), Flag.optional),
|
|
304
311
|
expiresAt: Flag.String("expires-at").pipe(Flag.withDescription("When the secret expires, any date string; it never does when left out"), Flag.optional)
|
|
305
312
|
}, ({ name, kind, host, expiresAt }) => Effect.gen(function* () {
|
package/dist/main.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { c as NotFound, d as claimsOf, m as ProviderUnavailable, o as Name, p as CurrentPrincipal, r as CredentialKind, s as NeedsSignIn, t as ClientId, u as UpstreamUnavailable } from "./shared-C56uw1xx.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,16 @@ 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 that knows which provider each name is for. A machine
|
|
61
|
+
* whose config names one refuses the sync.
|
|
62
|
+
*/
|
|
63
|
+
const TokenCredential = Schema.Struct({
|
|
64
|
+
type: Schema.Literal("token"),
|
|
65
|
+
...fromCredential,
|
|
66
|
+
token: Schema.String
|
|
67
|
+
});
|
|
68
|
+
/**
|
|
51
69
|
* The service sends typed credentials, never file contents or paths, so it cannot write
|
|
52
70
|
* anything on a machine but what the client knows how to place. The cost is that a new
|
|
53
71
|
* tool, or a placement fix, needs a client release, which new machines install. Each entry
|
|
@@ -64,7 +82,8 @@ const TypedCredential = Schema.Union([
|
|
|
64
82
|
GhCredential,
|
|
65
83
|
ClaudeCredential,
|
|
66
84
|
CodexCredential,
|
|
67
|
-
PiCredential
|
|
85
|
+
PiCredential,
|
|
86
|
+
TokenCredential
|
|
68
87
|
]);
|
|
69
88
|
/** The types this build knows, derived from the union so there is one list. */
|
|
70
89
|
const knownTypes = TypedCredential.members.map((member) => member.fields.type.literal);
|
|
@@ -75,39 +94,54 @@ const isKnownType = (type) => knownTypes.some((known) => known === type);
|
|
|
75
94
|
* schema must not land here, or a broken credential would be placed as an empty one.
|
|
76
95
|
*/
|
|
77
96
|
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
|
|
97
|
+
/** Everything the caller asked for, sent in full on every sync. */
|
|
79
98
|
const Delivery = Schema.Struct({ credentials: Schema.Array(Schema.Union([TypedCredential, UnknownCredential])) });
|
|
80
99
|
//#endregion
|
|
81
100
|
//#region ../../packages/api/src/machine.ts
|
|
101
|
+
/** A credential the calling key may read, without its value. */
|
|
102
|
+
const Grant = Schema.Struct({
|
|
103
|
+
name: Name,
|
|
104
|
+
kind: CredentialKind
|
|
105
|
+
});
|
|
82
106
|
/**
|
|
83
107
|
* What a machine key can do: read the credentials it is granted, by name, and nothing else.
|
|
84
|
-
* The
|
|
108
|
+
* The caller names what it wants; the grants on the server decide whether it may. A machine
|
|
109
|
+
* is one caller; a service such as clankerusage, holding a key of its own, is another.
|
|
85
110
|
*/
|
|
86
|
-
const Machine = [
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
111
|
+
const Machine = [
|
|
112
|
+
Action.make("sync", {
|
|
113
|
+
readOnly: false,
|
|
114
|
+
caller: CurrentPrincipal,
|
|
115
|
+
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.",
|
|
116
|
+
input: Schema.Struct({
|
|
117
|
+
clientId: ClientId,
|
|
118
|
+
credentials: Schema.Array(Name)
|
|
119
|
+
}),
|
|
120
|
+
success: Delivery,
|
|
121
|
+
error: NotFound
|
|
93
122
|
}),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
Action.make("codexLogin", {
|
|
124
|
+
readOnly: false,
|
|
125
|
+
caller: CurrentPrincipal,
|
|
126
|
+
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.",
|
|
127
|
+
input: Schema.Struct({
|
|
128
|
+
clientId: ClientId,
|
|
129
|
+
credential: Name
|
|
130
|
+
}),
|
|
131
|
+
success: CodexLogin,
|
|
132
|
+
error: [
|
|
133
|
+
NotFound,
|
|
134
|
+
NeedsSignIn,
|
|
135
|
+
UpstreamUnavailable
|
|
136
|
+
]
|
|
103
137
|
}),
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
138
|
+
Action.make("listGrants", {
|
|
139
|
+
readOnly: true,
|
|
140
|
+
caller: CurrentPrincipal,
|
|
141
|
+
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.",
|
|
142
|
+
success: Schema.Struct({ credentials: Schema.Array(Grant) })
|
|
143
|
+
})
|
|
144
|
+
];
|
|
111
145
|
/**
|
|
112
146
|
* Codex treats a 401 as permanent and stops retrying; every other failure is transient.
|
|
113
147
|
* The codes are the ones Codex classifies: `refresh_token_invalidated` reads as revoked.
|
|
@@ -582,6 +616,9 @@ const placements = {
|
|
|
582
616
|
remove: (config) => removePi(config.home)
|
|
583
617
|
}
|
|
584
618
|
};
|
|
619
|
+
/** What this client places; anything else delivered, a token or a type it does not know, is skipped. */
|
|
620
|
+
const isPlaceable = (credential) => Object.hasOwn(placements, credential.type);
|
|
621
|
+
const placeableTypes = knownTypes.filter((type) => Object.hasOwn(placements, type));
|
|
585
622
|
/** What the delivery carries of one type is placed; a type it does not carry is undone. */
|
|
586
623
|
const settle = (config, identity, known, type) => {
|
|
587
624
|
const credentials = known.filter((credential) => credential.type === type);
|
|
@@ -602,16 +639,17 @@ const slot = (credential) => {
|
|
|
602
639
|
}
|
|
603
640
|
};
|
|
604
641
|
/**
|
|
605
|
-
* Two credentials for one slot would leave the machine holding whichever came last
|
|
642
|
+
* Two credentials for one slot would leave the machine holding whichever came last: the
|
|
606
643
|
* whole sync is refused before anything is written.
|
|
607
644
|
*/
|
|
608
|
-
const
|
|
645
|
+
const oneEach = (placed) => {
|
|
609
646
|
const claimed = /* @__PURE__ */ new Map();
|
|
610
|
-
for (const credential of
|
|
647
|
+
for (const credential of placed) {
|
|
611
648
|
const other = claimed.get(slot(credential));
|
|
612
649
|
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
650
|
claimed.set(slot(credential), credential.credential);
|
|
614
651
|
}
|
|
652
|
+
return placed;
|
|
615
653
|
};
|
|
616
654
|
/**
|
|
617
655
|
* Idempotent: every credential the config names is fetched and placed every time, and every
|
|
@@ -626,15 +664,14 @@ const sync = async (config) => {
|
|
|
626
664
|
key: config.key
|
|
627
665
|
};
|
|
628
666
|
const delivery = await fetchDelivery(config, identity.clientId);
|
|
629
|
-
const known = delivery.credentials.filter(
|
|
630
|
-
refuseShared(known);
|
|
667
|
+
const known = oneEach(delivery.credentials.filter(isPlaceable));
|
|
631
668
|
const report = {
|
|
632
669
|
placed: new Array(),
|
|
633
670
|
removed: new Array(),
|
|
634
|
-
skipped: delivery.credentials.flatMap((
|
|
671
|
+
skipped: delivery.credentials.flatMap((credential) => isPlaceable(credential) ? [] : [credential.type]),
|
|
635
672
|
failed: new Array()
|
|
636
673
|
};
|
|
637
|
-
for (const type of
|
|
674
|
+
for (const type of placeableTypes) try {
|
|
638
675
|
const outcome = settle(config, identity, known, type);
|
|
639
676
|
if (outcome !== void 0) report[outcome].push(type);
|
|
640
677
|
} catch (error) {
|
|
@@ -684,7 +721,7 @@ const usage = `Usage: clankercreds <command>
|
|
|
684
721
|
`;
|
|
685
722
|
const runSync = async () => {
|
|
686
723
|
const report = await sync(loadConfig());
|
|
687
|
-
for (const type of report.skipped) console.error(`warning: skipped
|
|
724
|
+
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
725
|
for (const type of report.placed) console.log(`placed ${type}`);
|
|
689
726
|
for (const type of report.removed) console.log(`removed ${type}`);
|
|
690
727
|
for (const { type, message } of report.failed) console.error(`error: ${type}: ${message}`);
|
|
@@ -704,7 +741,7 @@ const commandFor = (name) => {
|
|
|
704
741
|
};
|
|
705
742
|
const main = async () => {
|
|
706
743
|
if (process.argv[2] === "admin") {
|
|
707
|
-
(await import("./admin-
|
|
744
|
+
(await import("./admin-DC0cTmmT.mjs")).run(process.argv.slice(2));
|
|
708
745
|
return;
|
|
709
746
|
}
|
|
710
747
|
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
|
+
const CredentialKind = 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 { LoginKind as a, NotFound as c, claimsOf as d, scopes as f, Host as i, SecretKind as l, ProviderUnavailable as m, Conflict as n, Name as o, CurrentPrincipal as p, CredentialKind as r, NeedsSignIn as s, ClientId as t, UpstreamUnavailable as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjermundgaraba/clankercreds",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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.4.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"
|