@vibe-commit/capture 0.4.5 → 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.
- package/README.md +44 -1
- package/dist/atomic_write.d.ts +14 -0
- package/dist/atomic_write.js +33 -0
- package/dist/atomic_write.js.map +1 -0
- package/dist/commands/auth.d.ts +2 -2
- package/dist/commands/auth.js +38 -11
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/browser_credential.d.ts +39 -12
- package/dist/commands/browser_credential.js +84 -11
- package/dist/commands/browser_credential.js.map +1 -1
- package/dist/commands/connect.d.ts +6 -2
- package/dist/commands/connect.js +65 -11
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/credential_problem.d.ts +43 -0
- package/dist/commands/credential_problem.js +81 -0
- package/dist/commands/credential_problem.js.map +1 -0
- package/dist/commands/device_credential.d.ts +47 -0
- package/dist/commands/device_credential.js +275 -0
- package/dist/commands/device_credential.js.map +1 -0
- package/dist/commands/finish_mint.d.ts +73 -0
- package/dist/commands/finish_mint.js +140 -0
- package/dist/commands/finish_mint.js.map +1 -0
- package/dist/commands/probe.d.ts +14 -0
- package/dist/commands/probe.js +29 -0
- package/dist/commands/probe.js.map +1 -0
- package/dist/commands/status.js +36 -52
- package/dist/commands/status.js.map +1 -1
- package/dist/copy/index.d.ts +1 -1
- package/dist/copy/index.js +1 -1
- package/dist/copy/index.js.map +1 -1
- package/dist/copy/strings.d.ts +175 -3
- package/dist/copy/strings.js +179 -3
- package/dist/copy/strings.js.map +1 -1
- package/dist/credential.d.ts +25 -0
- package/dist/credential.js +41 -3
- package/dist/credential.js.map +1 -1
- package/dist/credential_meta.d.ts +66 -0
- package/dist/credential_meta.js +164 -0
- package/dist/credential_meta.js.map +1 -0
- package/dist/device_mode.d.ts +18 -0
- package/dist/device_mode.js +28 -0
- package/dist/device_mode.js.map +1 -0
- package/dist/hooks/entry.d.ts +5 -0
- package/dist/hooks/entry.js +40 -5
- package/dist/hooks/entry.js.map +1 -1
- package/dist/index.js +28 -2
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +37 -0
- package/dist/install.js +29 -2
- package/dist/install.js.map +1 -1
- package/dist/oauth/device.d.ts +129 -0
- package/dist/oauth/device.js +194 -0
- package/dist/oauth/device.js.map +1 -0
- package/dist/oauth/discovery.d.ts +5 -0
- package/dist/oauth/discovery.js +6 -0
- package/dist/oauth/discovery.js.map +1 -1
- package/dist/oauth/mint.d.ts +18 -5
- package/dist/oauth/mint.js +18 -5
- package/dist/oauth/mint.js.map +1 -1
- package/dist/oauth/retire.d.ts +64 -0
- package/dist/oauth/retire.js +106 -0
- package/dist/oauth/retire.js.map +1 -0
- package/dist/oauth/signin.d.ts +12 -0
- package/dist/oauth/signin.js +5 -1
- package/dist/oauth/signin.js.map +1 -1
- package/dist/oauth/token.d.ts +1 -0
- package/dist/oauth/token.js +5 -0
- package/dist/oauth/token.js.map +1 -1
- package/dist/oauth/whoami.d.ts +55 -0
- package/dist/oauth/whoami.js +77 -0
- package/dist/oauth/whoami.js.map +1 -0
- package/dist/paths.d.ts +19 -0
- package/dist/paths.js +25 -0
- package/dist/paths.js.map +1 -1
- package/dist/post.d.ts +14 -0
- package/dist/post.js +15 -0
- package/dist/post.js.map +1 -1
- package/dist/state.d.ts +11 -0
- package/dist/state.js +33 -0
- package/dist/state.js.map +1 -1
- package/dist/system_message.d.ts +21 -2
- package/dist/system_message.js +37 -7
- package/dist/system_message.js.map +1 -1
- package/dist/term.d.ts +6 -0
- package/dist/term.js +15 -0
- package/dist/term.js.map +1 -1
- package/dist/update_nudge.d.ts +31 -0
- package/dist/update_nudge.js +127 -0
- package/dist/update_nudge.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/dist/install.js
CHANGED
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
* thing testable by handing in an `env`, which a subprocess-based version could
|
|
25
25
|
* only fake by building a directory of shims.
|
|
26
26
|
*/
|
|
27
|
-
import { accessSync, chmodSync, constants, mkdirSync, readFileSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
28
|
-
import { delimiter, isAbsolute, join, resolve } from "node:path";
|
|
27
|
+
import { accessSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
28
|
+
import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
|
|
29
29
|
import { gitProbe } from "./git.js";
|
|
30
30
|
/** The command name D55 kept. */
|
|
31
31
|
const BIN = "vibecommit";
|
|
@@ -52,6 +52,33 @@ export function classifyInstall(env, selfPath) {
|
|
|
52
52
|
return { kind: "ours" };
|
|
53
53
|
return { kind: "foreign", resolved: other ?? found };
|
|
54
54
|
}
|
|
55
|
+
/** The file the plugin repo pins its vendored bundle with, next to `bin/` (`claude-plugin`). */
|
|
56
|
+
const PLUGIN_BUNDLE_PIN = "capture-bundle.json";
|
|
57
|
+
export function installChannel(env, selfPath) {
|
|
58
|
+
const self = realpathOrNull(selfPath);
|
|
59
|
+
if (self === null)
|
|
60
|
+
return { kind: "unknown", command: BIN };
|
|
61
|
+
if (classifyInstall(env, selfPath).kind === "ours")
|
|
62
|
+
return { kind: "npm", command: BIN };
|
|
63
|
+
if (existsSync(join(dirname(dirname(self)), PLUGIN_BUNDLE_PIN))) {
|
|
64
|
+
// Quoted only when it has to be: a plugin cache path can hold a space.
|
|
65
|
+
return { kind: "plugin", command: /[\s"'$`\\]/.test(self) ? `node "${self.replace(/(["$`\\])/g, "\\$1")}"` : `node ${self}` };
|
|
66
|
+
}
|
|
67
|
+
return { kind: "npm", command: BIN };
|
|
68
|
+
}
|
|
69
|
+
/** What a user types to replace this machine's credential, for THIS install: `<channel command> auth`. */
|
|
70
|
+
export function authCommandFor(env, selfPath) {
|
|
71
|
+
return `${installChannel(env, selfPath).command} ${AUTH_VERB}`;
|
|
72
|
+
}
|
|
73
|
+
const AUTH_VERB = "auth";
|
|
74
|
+
/**
|
|
75
|
+
* What a user types to run the device sign-in again, for THIS install (`CR-234d`): `<channel command>
|
|
76
|
+
* connect --device`. The same channel rule as `authCommandFor`, so a plugin-only machine — which has no
|
|
77
|
+
* `vibecommit` on PATH — is not told to run a command that does not exist.
|
|
78
|
+
*/
|
|
79
|
+
export function connectDeviceCommandFor(env, selfPath) {
|
|
80
|
+
return `${installChannel(env, selfPath).command} connect --device`;
|
|
81
|
+
}
|
|
55
82
|
/** The first executable `name` on PATH, or null. */
|
|
56
83
|
export function resolveOnPath(env, name) {
|
|
57
84
|
const raw = env.PATH;
|
package/dist/install.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The device-authorization client — RFC 8628 for the capture CLI, `CR-234d`, D219.
|
|
3
|
+
*
|
|
4
|
+
* ## What the grant is FOR
|
|
5
|
+
*
|
|
6
|
+
* A machine with no browser (an SSH box, a devcontainer) cannot run the loopback sign-in. Here the machine
|
|
7
|
+
* asks for a code, shows it, and polls while the person approves on a device that DOES have a browser.
|
|
8
|
+
* What approval yields is not a session: it is a single-use MINT TICKET (`vcmt_…`, R4) that buys exactly one
|
|
9
|
+
* ingest credential at the mint route. Nothing is written to `session.json`.
|
|
10
|
+
*
|
|
11
|
+
* ```
|
|
12
|
+
* POST <device_authorization_endpoint> { client_id, machine_label, org_id? }
|
|
13
|
+
* 200 { device_code, user_code, verification_uri, verification_uri_complete, expires_in, interval }
|
|
14
|
+
* 429 { error: "rate_limited", retry_after } + Retry-After
|
|
15
|
+
*
|
|
16
|
+
* POST <token_endpoint> grant_type=urn:ietf:params:oauth:grant-type:device_code & device_code & client_id
|
|
17
|
+
* 400 authorization_pending | slow_down | access_denied | expired_token | invalid_grant | invalid_request
|
|
18
|
+
* 200 { access_token: "vcmt_…", token_type: "mint_ticket", expires_in, account, org_name }
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* ⚠ The endpoint is DISCOVERED (`discovery.ts`), never built from the issuer. And a poll answer this client
|
|
22
|
+
* does not recognise is `malformed`, never a quiet "keep waiting" — a loop that cannot tell a new error from
|
|
23
|
+
* a pending one polls a dead code until its deadline.
|
|
24
|
+
*
|
|
25
|
+
* @provenance vibecommit-mcp src/oauth/device.ts — the device_authorization and device_code-grant request and response shapes, retyped
|
|
26
|
+
* @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/device_authorization and the device_code grant on /oauth/token, retyped
|
|
27
|
+
*/
|
|
28
|
+
import { DeviceCode, MintTicket } from "../credential.js";
|
|
29
|
+
/** RFC 8628 §3.4. */
|
|
30
|
+
export declare const DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
31
|
+
/** The server's default poll spacing when it names none (RFC 8628 §3.2). */
|
|
32
|
+
export declare const DEFAULT_INTERVAL_S = 5;
|
|
33
|
+
/** `slow_down` adds this many seconds to the interval for the rest of the flow (RFC 8628 §3.5). */
|
|
34
|
+
export declare const SLOW_DOWN_STEP_S = 5;
|
|
35
|
+
/**
|
|
36
|
+
* The `user_code` as the person is shown it. Deliberately loose about the ALPHABET — the server owns that and
|
|
37
|
+
* a client copy of it is a second place for it to drift — but strict about the SHAPE, because this string is
|
|
38
|
+
* printed to a terminal: an escape sequence or a newline from a hostile server must never reach it.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* The server's `error_description` on an `access_denied` that is the 16C gate rather than a person's "no"
|
|
42
|
+
* (brief A7). Compared EXACTLY: a description this client does not recognise is an ordinary denial, which is
|
|
43
|
+
* the conservative reading — it shows the Denied block and changes nothing.
|
|
44
|
+
*/
|
|
45
|
+
export declare const GATE_OFF_DESCRIPTION = "code sign-in is off for this account";
|
|
46
|
+
export interface DeviceHttp {
|
|
47
|
+
readonly fetch: typeof fetch;
|
|
48
|
+
readonly timeoutMs: number;
|
|
49
|
+
}
|
|
50
|
+
export interface DeviceGrant {
|
|
51
|
+
readonly deviceCode: DeviceCode;
|
|
52
|
+
/** `WDJB-MJHT`, upper case, as shown. */
|
|
53
|
+
readonly userCode: string;
|
|
54
|
+
/** The bare page. ALWAYS printed (RFC 8628 §3.3.1). */
|
|
55
|
+
readonly verificationUri: string;
|
|
56
|
+
/** The page with the code prefilled. Opened, never relied on. */
|
|
57
|
+
readonly verificationUriComplete: string;
|
|
58
|
+
readonly expiresInS: number;
|
|
59
|
+
readonly intervalS: number;
|
|
60
|
+
}
|
|
61
|
+
export type DeviceStart = {
|
|
62
|
+
readonly kind: "ok";
|
|
63
|
+
readonly grant: DeviceGrant;
|
|
64
|
+
}
|
|
65
|
+
/** 429. `retryAfterS` is the server's word, or null when it gave none. */
|
|
66
|
+
| {
|
|
67
|
+
readonly kind: "rate-limited";
|
|
68
|
+
readonly retryAfterS: number | null;
|
|
69
|
+
}
|
|
70
|
+
/** Any other refusal, with the server's code (`unauthorized_client`, `invalid_request`, `server_error`…). */
|
|
71
|
+
| {
|
|
72
|
+
readonly kind: "refused";
|
|
73
|
+
readonly status: number;
|
|
74
|
+
readonly error: string;
|
|
75
|
+
} | {
|
|
76
|
+
readonly kind: "unreachable";
|
|
77
|
+
} | {
|
|
78
|
+
readonly kind: "malformed";
|
|
79
|
+
};
|
|
80
|
+
export interface DeviceStartRequest {
|
|
81
|
+
readonly machineLabel: string;
|
|
82
|
+
/** The org the credential this machine replaces belongs to (15A) — a HINT; the approver decides. */
|
|
83
|
+
readonly orgId?: string;
|
|
84
|
+
}
|
|
85
|
+
/** Ask the server for a device code. */
|
|
86
|
+
export declare function startDeviceAuthorization(endpoint: string, request: DeviceStartRequest, http: DeviceHttp): Promise<DeviceStart>;
|
|
87
|
+
export type DevicePoll = {
|
|
88
|
+
readonly kind: "ticket";
|
|
89
|
+
readonly ticket: MintTicket;
|
|
90
|
+
readonly expiresInS: number | null;
|
|
91
|
+
/** The approver's GitHub handle, or null. Carried for the confirmation when whoami(new) is unanswered (A4 §3). */
|
|
92
|
+
readonly account: string | null;
|
|
93
|
+
/** The workspace the approver chose. */
|
|
94
|
+
readonly orgName: string | null;
|
|
95
|
+
} | {
|
|
96
|
+
readonly kind: "pending";
|
|
97
|
+
}
|
|
98
|
+
/** RFC 8628 §3.5: add `SLOW_DOWN_STEP_S` to the interval, permanently. */
|
|
99
|
+
| {
|
|
100
|
+
readonly kind: "slow-down";
|
|
101
|
+
} | {
|
|
102
|
+
readonly kind: "denied";
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* `access_denied` whose description says the account's code sign-in switch is off (16C, brief A7). The
|
|
106
|
+
* gate is enforced when the ticket is issued, so a switch turned off AFTER the approval lands here.
|
|
107
|
+
*/
|
|
108
|
+
| {
|
|
109
|
+
readonly kind: "code-signin-off";
|
|
110
|
+
} | {
|
|
111
|
+
readonly kind: "expired";
|
|
112
|
+
}
|
|
113
|
+
/** `invalid_grant`: the code is unknown or already spent — not something waiting longer fixes. */
|
|
114
|
+
| {
|
|
115
|
+
readonly kind: "invalid";
|
|
116
|
+
} | {
|
|
117
|
+
readonly kind: "rate-limited";
|
|
118
|
+
readonly retryAfterS: number | null;
|
|
119
|
+
}
|
|
120
|
+
/** A server fault. The loop tolerates a few in a row, because a deploy is an ordinary reason for one. */
|
|
121
|
+
| {
|
|
122
|
+
readonly kind: "server-error";
|
|
123
|
+
} | {
|
|
124
|
+
readonly kind: "unreachable";
|
|
125
|
+
} | {
|
|
126
|
+
readonly kind: "malformed";
|
|
127
|
+
};
|
|
128
|
+
/** One poll of the token endpoint. */
|
|
129
|
+
export declare function pollDeviceToken(tokenEndpoint: string, deviceCode: DeviceCode, http: DeviceHttp): Promise<DevicePoll>;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The device-authorization client — RFC 8628 for the capture CLI, `CR-234d`, D219.
|
|
3
|
+
*
|
|
4
|
+
* ## What the grant is FOR
|
|
5
|
+
*
|
|
6
|
+
* A machine with no browser (an SSH box, a devcontainer) cannot run the loopback sign-in. Here the machine
|
|
7
|
+
* asks for a code, shows it, and polls while the person approves on a device that DOES have a browser.
|
|
8
|
+
* What approval yields is not a session: it is a single-use MINT TICKET (`vcmt_…`, R4) that buys exactly one
|
|
9
|
+
* ingest credential at the mint route. Nothing is written to `session.json`.
|
|
10
|
+
*
|
|
11
|
+
* ```
|
|
12
|
+
* POST <device_authorization_endpoint> { client_id, machine_label, org_id? }
|
|
13
|
+
* 200 { device_code, user_code, verification_uri, verification_uri_complete, expires_in, interval }
|
|
14
|
+
* 429 { error: "rate_limited", retry_after } + Retry-After
|
|
15
|
+
*
|
|
16
|
+
* POST <token_endpoint> grant_type=urn:ietf:params:oauth:grant-type:device_code & device_code & client_id
|
|
17
|
+
* 400 authorization_pending | slow_down | access_denied | expired_token | invalid_grant | invalid_request
|
|
18
|
+
* 200 { access_token: "vcmt_…", token_type: "mint_ticket", expires_in, account, org_name }
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* ⚠ The endpoint is DISCOVERED (`discovery.ts`), never built from the issuer. And a poll answer this client
|
|
22
|
+
* does not recognise is `malformed`, never a quiet "keep waiting" — a loop that cannot tell a new error from
|
|
23
|
+
* a pending one polls a dead code until its deadline.
|
|
24
|
+
*
|
|
25
|
+
* @provenance vibecommit-mcp src/oauth/device.ts — the device_authorization and device_code-grant request and response shapes, retyped
|
|
26
|
+
* @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/device_authorization and the device_code grant on /oauth/token, retyped
|
|
27
|
+
*/
|
|
28
|
+
import { DeviceCode, MintTicket, isMintTicketShape } from "../credential.js";
|
|
29
|
+
import { isAllowedIngestUrl } from "../post.js";
|
|
30
|
+
import { CLIENT_ID } from "./token.js";
|
|
31
|
+
/** RFC 8628 §3.4. */
|
|
32
|
+
export const DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
33
|
+
/** The server's default poll spacing when it names none (RFC 8628 §3.2). */
|
|
34
|
+
export const DEFAULT_INTERVAL_S = 5;
|
|
35
|
+
/** `slow_down` adds this many seconds to the interval for the rest of the flow (RFC 8628 §3.5). */
|
|
36
|
+
export const SLOW_DOWN_STEP_S = 5;
|
|
37
|
+
/**
|
|
38
|
+
* The `user_code` as the person is shown it. Deliberately loose about the ALPHABET — the server owns that and
|
|
39
|
+
* a client copy of it is a second place for it to drift — but strict about the SHAPE, because this string is
|
|
40
|
+
* printed to a terminal: an escape sequence or a newline from a hostile server must never reach it.
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* The server's `error_description` on an `access_denied` that is the 16C gate rather than a person's "no"
|
|
44
|
+
* (brief A7). Compared EXACTLY: a description this client does not recognise is an ordinary denial, which is
|
|
45
|
+
* the conservative reading — it shows the Denied block and changes nothing.
|
|
46
|
+
*/
|
|
47
|
+
export const GATE_OFF_DESCRIPTION = "code sign-in is off for this account";
|
|
48
|
+
const USER_CODE_SHAPE = /^[A-Z0-9]{3,6}-[A-Z0-9]{3,6}$/;
|
|
49
|
+
async function postWithTimeout(url, init, http) {
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const timer = setTimeout(() => controller.abort(), http.timeoutMs);
|
|
52
|
+
try {
|
|
53
|
+
const res = await http.fetch(url, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: { "content-type": init.contentType, accept: "application/json" },
|
|
56
|
+
body: init.body,
|
|
57
|
+
signal: controller.signal,
|
|
58
|
+
// Same reason as every other lane: a followed redirect carries the body somewhere unconfigured.
|
|
59
|
+
redirect: "error",
|
|
60
|
+
});
|
|
61
|
+
return { res };
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function readJson(res) {
|
|
71
|
+
try {
|
|
72
|
+
const body = (await res.json());
|
|
73
|
+
return body !== null && typeof body === "object" && !Array.isArray(body) ? body : null;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** `Retry-After` as whole seconds, or null. `Number(null)` is 0, which is why this does not use it. */
|
|
80
|
+
function retryAfterSeconds(res, body) {
|
|
81
|
+
const fromBody = body?.retry_after;
|
|
82
|
+
if (typeof fromBody === "number" && Number.isFinite(fromBody) && fromBody > 0)
|
|
83
|
+
return Math.ceil(fromBody);
|
|
84
|
+
const header = res.headers.get("retry-after");
|
|
85
|
+
if (header === null)
|
|
86
|
+
return null;
|
|
87
|
+
const n = Number(header);
|
|
88
|
+
return Number.isFinite(n) && n > 0 ? Math.ceil(n) : null;
|
|
89
|
+
}
|
|
90
|
+
function positiveInt(value) {
|
|
91
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : null;
|
|
92
|
+
}
|
|
93
|
+
/** Ask the server for a device code. */
|
|
94
|
+
export async function startDeviceAuthorization(endpoint, request, http) {
|
|
95
|
+
const sent = await postWithTimeout(endpoint, {
|
|
96
|
+
contentType: "application/json",
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
client_id: CLIENT_ID,
|
|
99
|
+
machine_label: request.machineLabel,
|
|
100
|
+
...(request.orgId === undefined ? {} : { org_id: request.orgId }),
|
|
101
|
+
}),
|
|
102
|
+
}, http);
|
|
103
|
+
if (sent === null)
|
|
104
|
+
return { kind: "unreachable" };
|
|
105
|
+
const { res } = sent;
|
|
106
|
+
const doc = await readJson(res);
|
|
107
|
+
if (res.status === 429)
|
|
108
|
+
return { kind: "rate-limited", retryAfterS: retryAfterSeconds(res, doc) };
|
|
109
|
+
if (res.status !== 200) {
|
|
110
|
+
return { kind: "refused", status: res.status, error: typeof doc?.error === "string" ? doc.error : "" };
|
|
111
|
+
}
|
|
112
|
+
if (doc === null)
|
|
113
|
+
return { kind: "malformed" };
|
|
114
|
+
const { device_code, user_code, verification_uri, verification_uri_complete } = doc;
|
|
115
|
+
const expiresInS = positiveInt(doc.expires_in);
|
|
116
|
+
if (typeof device_code !== "string" ||
|
|
117
|
+
device_code === "" ||
|
|
118
|
+
typeof user_code !== "string" ||
|
|
119
|
+
!USER_CODE_SHAPE.test(user_code.toUpperCase()) ||
|
|
120
|
+
typeof verification_uri !== "string" ||
|
|
121
|
+
typeof verification_uri_complete !== "string" ||
|
|
122
|
+
expiresInS === null) {
|
|
123
|
+
return { kind: "malformed" };
|
|
124
|
+
}
|
|
125
|
+
// Both URIs are printed or opened on the person's machine: https, or http on a loopback, and nothing else.
|
|
126
|
+
if (!isAllowedIngestUrl(verification_uri) || !isAllowedIngestUrl(verification_uri_complete)) {
|
|
127
|
+
return { kind: "malformed" };
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
kind: "ok",
|
|
131
|
+
grant: {
|
|
132
|
+
deviceCode: new DeviceCode(device_code),
|
|
133
|
+
userCode: user_code.toUpperCase(),
|
|
134
|
+
verificationUri: verification_uri,
|
|
135
|
+
verificationUriComplete: verification_uri_complete,
|
|
136
|
+
expiresInS,
|
|
137
|
+
intervalS: positiveInt(doc.interval) ?? DEFAULT_INTERVAL_S,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/** One poll of the token endpoint. */
|
|
142
|
+
export async function pollDeviceToken(tokenEndpoint, deviceCode, http) {
|
|
143
|
+
const sent = await postWithTimeout(tokenEndpoint, {
|
|
144
|
+
contentType: "application/x-www-form-urlencoded",
|
|
145
|
+
body: new URLSearchParams({
|
|
146
|
+
grant_type: DEVICE_GRANT_TYPE,
|
|
147
|
+
// The request boundary — the one place this secret's plaintext is used.
|
|
148
|
+
device_code: deviceCode.expose(),
|
|
149
|
+
client_id: CLIENT_ID,
|
|
150
|
+
}).toString(),
|
|
151
|
+
}, http);
|
|
152
|
+
if (sent === null)
|
|
153
|
+
return { kind: "unreachable" };
|
|
154
|
+
const { res } = sent;
|
|
155
|
+
const doc = await readJson(res);
|
|
156
|
+
if (res.status === 429)
|
|
157
|
+
return { kind: "rate-limited", retryAfterS: retryAfterSeconds(res, doc) };
|
|
158
|
+
if (res.status >= 500)
|
|
159
|
+
return { kind: "server-error" };
|
|
160
|
+
if (res.status === 200) {
|
|
161
|
+
if (doc === null)
|
|
162
|
+
return { kind: "malformed" };
|
|
163
|
+
const token = doc.access_token;
|
|
164
|
+
// Both halves of the class check: the prefix, and the type the server says it is. A bearer that is
|
|
165
|
+
// not a mint ticket must never be spent at the mint route as one.
|
|
166
|
+
if (typeof token !== "string" || !isMintTicketShape(token) || doc.token_type !== "mint_ticket") {
|
|
167
|
+
return { kind: "malformed" };
|
|
168
|
+
}
|
|
169
|
+
const text = (v) => (typeof v === "string" && v !== "" ? v : null);
|
|
170
|
+
return {
|
|
171
|
+
kind: "ticket",
|
|
172
|
+
ticket: new MintTicket(token),
|
|
173
|
+
expiresInS: positiveInt(doc.expires_in),
|
|
174
|
+
account: text(doc.account),
|
|
175
|
+
orgName: text(doc.org_name),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
switch (typeof doc?.error === "string" ? doc.error : "") {
|
|
179
|
+
case "authorization_pending":
|
|
180
|
+
return { kind: "pending" };
|
|
181
|
+
case "slow_down":
|
|
182
|
+
return { kind: "slow-down" };
|
|
183
|
+
case "access_denied":
|
|
184
|
+
return doc?.error_description === GATE_OFF_DESCRIPTION ? { kind: "code-signin-off" } : { kind: "denied" };
|
|
185
|
+
case "expired_token":
|
|
186
|
+
return { kind: "expired" };
|
|
187
|
+
case "invalid_grant":
|
|
188
|
+
return { kind: "invalid" };
|
|
189
|
+
default:
|
|
190
|
+
// `invalid_request`, `unauthorized_client`, a body with no `error`, a 3xx/4xx nobody documented.
|
|
191
|
+
return { kind: "malformed" };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../src/oauth/device.ts"],"names":[],"mappings":"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"}
|
|
@@ -59,6 +59,11 @@ export interface AuthorizationServerMetadata {
|
|
|
59
59
|
readonly issuer: string;
|
|
60
60
|
readonly authorization_endpoint: string;
|
|
61
61
|
readonly token_endpoint: string;
|
|
62
|
+
/**
|
|
63
|
+
* RFC 8628 §4, `CR-234`. OPTIONAL: a server that predates the device grant does not publish it, and
|
|
64
|
+
* the browser flow does not need it. Like the other two it is DISCOVERED, never built from the issuer.
|
|
65
|
+
*/
|
|
66
|
+
readonly device_authorization_endpoint?: string;
|
|
62
67
|
readonly code_challenge_methods_supported?: readonly string[];
|
|
63
68
|
}
|
|
64
69
|
export type Discovery = {
|
package/dist/oauth/discovery.js
CHANGED
|
@@ -144,6 +144,11 @@ export async function discover(resourceUrl, deps) {
|
|
|
144
144
|
return { kind: "malformed", url: asUrl, detail: "insecure_endpoint" };
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
// Optional, but if it is published it takes the same rule: the person's machine code and label go there.
|
|
148
|
+
const deviceEndpoint = doc.device_authorization_endpoint;
|
|
149
|
+
if (deviceEndpoint !== undefined && (typeof deviceEndpoint !== "string" || !isAllowedIngestUrl(deviceEndpoint))) {
|
|
150
|
+
return { kind: "malformed", url: asUrl, detail: "insecure_endpoint" };
|
|
151
|
+
}
|
|
147
152
|
// Absent means "unstated", and RFC 8414 §2 says a client that needs S256 must
|
|
148
153
|
// not assume it. We need it: it is the only method this client can produce.
|
|
149
154
|
const methods = doc.code_challenge_methods_supported;
|
|
@@ -156,6 +161,7 @@ export async function discover(resourceUrl, deps) {
|
|
|
156
161
|
issuer,
|
|
157
162
|
authorization_endpoint: doc.authorization_endpoint,
|
|
158
163
|
token_endpoint: doc.token_endpoint,
|
|
164
|
+
...(typeof deviceEndpoint === "string" ? { device_authorization_endpoint: deviceEndpoint } : {}),
|
|
159
165
|
code_challenge_methods_supported: methods,
|
|
160
166
|
},
|
|
161
167
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/oauth/discovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,qBAAqB;AACrB,MAAM,CAAC,MAAM,uBAAuB,GAAG,uCAAuC,CAAC;AAC/E,mBAAmB;AACnB,MAAM,CAAC,MAAM,yBAAyB,GAAG,yCAAyC,CAAC;AAEnF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,+BAA+B,CAAC;AAE/D,wFAAwF;AACxF,MAAM,UAAU,MAAM,CAAC,GAAsB;IAC3C,MAAM,QAAQ,GAAG,GAAG,CAAC,kBAAkB,CAAC;IACxC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,eAAe,CAAC;IAC7E,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAClC,OAAO,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/oauth/discovery.ts"],"names":[],"mappings":"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"}
|
package/dist/oauth/mint.d.ts
CHANGED
|
@@ -31,13 +31,14 @@
|
|
|
31
31
|
* 403 { error: "not_a_member" } · 400 · 429 · 500 refused; no retry helps
|
|
32
32
|
* ```
|
|
33
33
|
*
|
|
34
|
-
* ⚠
|
|
35
|
-
*
|
|
36
|
-
* and its expiry is the server's to
|
|
34
|
+
* ⚠ The `token` field is the only one a mint cannot do without. `credential.id`,
|
|
35
|
+
* `credential.expires_at` and `account` are read when present and never required
|
|
36
|
+
* (`MintFacts`): the credential is opaque and its expiry is the server's to
|
|
37
|
+
* enforce, so these only feed the sidecar the client says "expired" from.
|
|
37
38
|
*
|
|
38
39
|
* @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/ingest-credential request and response shapes, retyped
|
|
39
40
|
*/
|
|
40
|
-
import { IngestCredential, type AccessToken } from "../credential.js";
|
|
41
|
+
import { IngestCredential, type AccessToken, type MintTicket } from "../credential.js";
|
|
41
42
|
/** Same origin as `/mcp` — one host, one issuer (D73). */
|
|
42
43
|
export declare const MINT_PATH = "/oauth/ingest-credential";
|
|
43
44
|
/** The table's `length(label) between 1 and 128`, mirrored so the server never has to 400. */
|
|
@@ -50,11 +51,23 @@ export declare function mintUrl(mcpEndpoint: string): string;
|
|
|
50
51
|
* the hostname is what they would have typed into the web form anyway.
|
|
51
52
|
*/
|
|
52
53
|
export declare function machineLabel(hostname: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* What the 201 says about the row it just created — all of it OPTIONAL, because a server older than
|
|
56
|
+
* the `account` field omits it and the credential is still good. The token is the only field this
|
|
57
|
+
* client cannot do without; these feed the credential-meta sidecar and the 5B block (`R8`, `R10`).
|
|
58
|
+
*/
|
|
59
|
+
export interface MintFacts {
|
|
60
|
+
readonly credentialId: string | null;
|
|
61
|
+
readonly expiresAt: string | null;
|
|
62
|
+
/** The approver's GitHub handle, or null. */
|
|
63
|
+
readonly account: string | null;
|
|
64
|
+
}
|
|
53
65
|
export type MintOutcome =
|
|
54
66
|
/** Minted. Not yet saved — the caller writes it through `saveCredential`. */
|
|
55
67
|
{
|
|
56
68
|
readonly kind: "ok";
|
|
57
69
|
readonly credential: IngestCredential;
|
|
70
|
+
readonly facts: MintFacts;
|
|
58
71
|
}
|
|
59
72
|
/** 401. A FIRST-CLASS outcome so the caller refreshes once and retries once. */
|
|
60
73
|
| {
|
|
@@ -94,4 +107,4 @@ export interface MintDeps {
|
|
|
94
107
|
* refused before the server touches the table, so a retry cannot leave two live
|
|
95
108
|
* credentials where the user expected one.
|
|
96
109
|
*/
|
|
97
|
-
export declare function requestMint(endpoint: string, access: AccessToken, label: string, deps: MintDeps): Promise<MintOutcome>;
|
|
110
|
+
export declare function requestMint(endpoint: string, access: AccessToken | MintTicket, label: string, deps: MintDeps): Promise<MintOutcome>;
|
package/dist/oauth/mint.js
CHANGED
|
@@ -31,9 +31,10 @@
|
|
|
31
31
|
* 403 { error: "not_a_member" } · 400 · 429 · 500 refused; no retry helps
|
|
32
32
|
* ```
|
|
33
33
|
*
|
|
34
|
-
* ⚠
|
|
35
|
-
*
|
|
36
|
-
* and its expiry is the server's to
|
|
34
|
+
* ⚠ The `token` field is the only one a mint cannot do without. `credential.id`,
|
|
35
|
+
* `credential.expires_at` and `account` are read when present and never required
|
|
36
|
+
* (`MintFacts`): the credential is opaque and its expiry is the server's to
|
|
37
|
+
* enforce, so these only feed the sidecar the client says "expired" from.
|
|
37
38
|
*
|
|
38
39
|
* @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/ingest-credential request and response shapes, retyped
|
|
39
40
|
*/
|
|
@@ -71,7 +72,10 @@ export function machineLabel(hostname) {
|
|
|
71
72
|
* refused before the server touches the table, so a retry cannot leave two live
|
|
72
73
|
* credentials where the user expected one.
|
|
73
74
|
*/
|
|
74
|
-
export async function requestMint(endpoint,
|
|
75
|
+
export async function requestMint(endpoint,
|
|
76
|
+
// A session's access token (the loopback path) or a device approval's `vcmt_` ticket (`CR-234`): the
|
|
77
|
+
// route takes either, and this function only ever spends it as the bearer.
|
|
78
|
+
access, label, deps) {
|
|
75
79
|
const controller = new AbortController();
|
|
76
80
|
const timer = setTimeout(() => controller.abort(), deps.timeoutMs);
|
|
77
81
|
let res;
|
|
@@ -115,6 +119,15 @@ export async function requestMint(endpoint, access, label, deps) {
|
|
|
115
119
|
}
|
|
116
120
|
if (typeof doc.token !== "string" || !MINTED_SHAPE.test(doc.token))
|
|
117
121
|
return { kind: "malformed" };
|
|
118
|
-
|
|
122
|
+
const text = (v) => (typeof v === "string" && v !== "" ? v : null);
|
|
123
|
+
return {
|
|
124
|
+
kind: "ok",
|
|
125
|
+
credential: new IngestCredential(doc.token, "file"),
|
|
126
|
+
facts: {
|
|
127
|
+
credentialId: text(doc.credential?.id),
|
|
128
|
+
expiresAt: text(doc.credential?.expires_at),
|
|
129
|
+
account: text(doc.account),
|
|
130
|
+
},
|
|
131
|
+
};
|
|
119
132
|
}
|
|
120
133
|
//# sourceMappingURL=mint.js.map
|
package/dist/oauth/mint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mint.js","sourceRoot":"","sources":["../../src/oauth/mint.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mint.js","sourceRoot":"","sources":["../../src/oauth/mint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `POST /oauth/ingest-credential/retire` — retire the credential this one replaces (G4).
|
|
3
|
+
*
|
|
4
|
+
* ```
|
|
5
|
+
* POST <mcp origin>/oauth/ingest-credential/retire
|
|
6
|
+
* Authorization: Bearer <the NEW vcik_> { "old_token": "vcik_…" }
|
|
7
|
+
*
|
|
8
|
+
* 200 { "retired": true } the old credential was revoked with reason 'rotated'
|
|
9
|
+
* 200 { "retired": false } the ONE uniform answer for not-found / other user / other org /
|
|
10
|
+
* already revoked / same token — the server never says which
|
|
11
|
+
* 401 opaque · 400 { "error": "invalid_request" } · 429 retry-after · 404 an older server
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* ## Self-revoke (brief A3)
|
|
15
|
+
*
|
|
16
|
+
* The same route, body exactly `{ "self": true }`, Bearer = the credential to revoke: the bearer's own
|
|
17
|
+
* row is revoked (`user_revoke`) and the answer is 200 `{ "retired": true }`. It exists for the one
|
|
18
|
+
* case the two-token form cannot serve: a re-auth the user DECLINES after the browser landed in a
|
|
19
|
+
* different workspace. The credential just minted there is the one to remove, and the only credential
|
|
20
|
+
* this machine holds in that workspace is that one. A 404 (a server without it) or any failure leaves
|
|
21
|
+
* the credential live and unused, and the caller says where to revoke it.
|
|
22
|
+
*
|
|
23
|
+
* Proof of possession: the caller holds BOTH tokens, over the same TLS every ingest already uses.
|
|
24
|
+
* The old token travels in the body, never in argv or a URL. A 404 means "this server cannot
|
|
25
|
+
* retire", which the caller prints as "unknown" rather than as a failure — nothing is lost either
|
|
26
|
+
* way, the old row simply lives until it expires and shows in Settings.
|
|
27
|
+
*
|
|
28
|
+
* ⛔ `old_token` IS A SECRET, so nothing in this module renders a response body or an error.
|
|
29
|
+
*
|
|
30
|
+
* @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/ingest-credential/retire, retyped
|
|
31
|
+
*/
|
|
32
|
+
import type { IngestCredential } from "../credential.js";
|
|
33
|
+
export declare const RETIRE_PATH = "/oauth/ingest-credential/retire";
|
|
34
|
+
export type Retire =
|
|
35
|
+
/** The server revoked the old credential. */
|
|
36
|
+
{
|
|
37
|
+
readonly kind: "retired";
|
|
38
|
+
}
|
|
39
|
+
/** 200 `retired: false` — nothing was revoked, and the server will not say why. */
|
|
40
|
+
| {
|
|
41
|
+
readonly kind: "not-retired";
|
|
42
|
+
}
|
|
43
|
+
/** 404 — a server that predates the route. */
|
|
44
|
+
| {
|
|
45
|
+
readonly kind: "unknown";
|
|
46
|
+
}
|
|
47
|
+
/** Anything else: 401, 400, 429, 5xx, a timeout, a body off-contract. */
|
|
48
|
+
| {
|
|
49
|
+
readonly kind: "failed";
|
|
50
|
+
};
|
|
51
|
+
export interface RetireDeps {
|
|
52
|
+
readonly fetch: typeof fetch;
|
|
53
|
+
readonly timeoutMs?: number;
|
|
54
|
+
}
|
|
55
|
+
export declare function retireUrl(mcpEndpoint: string): string;
|
|
56
|
+
export declare function retireCredential(mcpEndpoint: string, replacement: IngestCredential, old: IngestCredential, deps: RetireDeps): Promise<Retire>;
|
|
57
|
+
/**
|
|
58
|
+
* Revoke `credential` with itself as the bearer — see "Self-revoke" above.
|
|
59
|
+
*
|
|
60
|
+
* `retired` only on a clean 200 `{ retired: true }`; a 404 is `unknown` (an older server); everything
|
|
61
|
+
* else — 401, 400, 429, 5xx, a timeout, a body off-contract — is `failed`. Never throws, and the
|
|
62
|
+
* result carries nothing of the credential.
|
|
63
|
+
*/
|
|
64
|
+
export declare function selfRevokeCredential(mcpEndpoint: string, credential: IngestCredential, deps: RetireDeps): Promise<Retire>;
|