@ritualai/cli 0.7.3 → 0.7.6
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 +36 -1
- package/dist/commands/init.js +174 -43
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.js +120 -13
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.js +55 -8
- package/dist/commands/logout.js.map +1 -1
- package/dist/index.js +8 -66
- package/dist/index.js.map +1 -1
- package/dist/lib/auth-flow.js +1 -1
- package/dist/lib/auth-flow.js.map +1 -1
- package/dist/lib/colors.js +182 -0
- package/dist/lib/colors.js.map +1 -0
- package/dist/lib/config.js +30 -17
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/credentials-backup.js +282 -0
- package/dist/lib/credentials-backup.js.map +1 -0
- package/dist/lib/identity-banner.js +88 -0
- package/dist/lib/identity-banner.js.map +1 -0
- package/dist/lib/oidc.js +55 -0
- package/dist/lib/oidc.js.map +1 -1
- package/dist/lib/package-info.js +76 -0
- package/dist/lib/package-info.js.map +1 -0
- package/dist/lib/welcome-banner.js +290 -0
- package/dist/lib/welcome-banner.js.map +1 -0
- package/package.json +1 -1
- package/skills/claude-code/ritual/agents/openai.yaml +2 -2
- package/skills/claude-code/ritual/manifest.json +57 -0
- package/skills/claude-code/ritual/references/build-flow.md +174 -105
- package/skills/claude-code/ritual/references/cli-output-contract.md +89 -0
- package/skills/claude-code/ritual/references/context-pulse-flow.md +1 -1
- package/skills/claude-code/ritual/references/lineage-flow.md +7 -6
- package/skills/claude-code/ritual/references/resume-flow.md +4 -4
- package/skills/codex/ritual/agents/openai.yaml +2 -2
- package/skills/codex/ritual/manifest.json +57 -0
- package/skills/codex/ritual/references/build-flow.md +174 -105
- package/skills/codex/ritual/references/cli-output-contract.md +89 -0
- package/skills/codex/ritual/references/context-pulse-flow.md +1 -1
- package/skills/codex/ritual/references/lineage-flow.md +7 -6
- package/skills/codex/ritual/references/resume-flow.md +4 -4
- package/skills/cursor/ritual/agents/openai.yaml +2 -2
- package/skills/cursor/ritual/manifest.json +57 -0
- package/skills/cursor/ritual/references/build-flow.md +174 -105
- package/skills/cursor/ritual/references/cli-output-contract.md +89 -0
- package/skills/cursor/ritual/references/context-pulse-flow.md +1 -1
- package/skills/cursor/ritual/references/lineage-flow.md +7 -6
- package/skills/cursor/ritual/references/resume-flow.md +4 -4
- package/skills/gemini/ritual/agents/openai.yaml +2 -2
- package/skills/gemini/ritual/manifest.json +57 -0
- package/skills/gemini/ritual/references/build-flow.md +174 -105
- package/skills/gemini/ritual/references/cli-output-contract.md +89 -0
- package/skills/gemini/ritual/references/context-pulse-flow.md +1 -1
- package/skills/gemini/ritual/references/lineage-flow.md +7 -6
- package/skills/gemini/ritual/references/resume-flow.md +4 -4
- package/skills/kiro/ritual/agents/openai.yaml +2 -2
- package/skills/kiro/ritual/manifest.json +57 -0
- package/skills/kiro/ritual/references/build-flow.md +174 -105
- package/skills/kiro/ritual/references/cli-output-contract.md +89 -0
- package/skills/kiro/ritual/references/context-pulse-flow.md +1 -1
- package/skills/kiro/ritual/references/lineage-flow.md +7 -6
- package/skills/kiro/ritual/references/resume-flow.md +4 -4
- package/skills/vscode/ritual/agents/openai.yaml +2 -2
- package/skills/vscode/ritual/manifest.json +57 -0
- package/skills/vscode/ritual/references/build-flow.md +174 -105
- package/skills/vscode/ritual/references/cli-output-contract.md +89 -0
- package/skills/vscode/ritual/references/context-pulse-flow.md +1 -1
- package/skills/vscode/ritual/references/lineage-flow.md +7 -6
- package/skills/vscode/ritual/references/resume-flow.md +4 -4
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._writeFileSyncForFallback = void 0;
|
|
4
|
+
exports.backupCredentials = backupCredentials;
|
|
5
|
+
exports.getInitLockPath = getInitLockPath;
|
|
6
|
+
exports.acquireInitLock = acquireInitLock;
|
|
7
|
+
exports.beginCredentialSwap = beginCredentialSwap;
|
|
8
|
+
exports.writeFileMode0600 = writeFileMode0600;
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
Object.defineProperty(exports, "_writeFileSyncForFallback", { enumerable: true, get: function () { return node_fs_1.writeFileSync; } });
|
|
11
|
+
const node_crypto_1 = require("node:crypto");
|
|
12
|
+
const node_path_1 = require("node:path");
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
/**
|
|
15
|
+
* Create an atomic backup of the live credentials file.
|
|
16
|
+
*
|
|
17
|
+
* If no live credentials exist, returns a handle whose `created` is
|
|
18
|
+
* false — `restore()` will then delete any newly-written credentials
|
|
19
|
+
* file instead of restoring a non-existent backup.
|
|
20
|
+
*
|
|
21
|
+
* Caller MUST invoke either `commit()` or `restore()` on the returned
|
|
22
|
+
* handle exactly once.
|
|
23
|
+
*/
|
|
24
|
+
function backupCredentials() {
|
|
25
|
+
const credsPath = (0, config_1.getCredentialsPath)();
|
|
26
|
+
const liveExists = (0, node_fs_1.existsSync)(credsPath);
|
|
27
|
+
const backupPath = liveExists ? mintBackupPath(credsPath) : null;
|
|
28
|
+
if (backupPath) {
|
|
29
|
+
// copyFileSync preserves mode bits from source on POSIX, but
|
|
30
|
+
// `saveCredentials` should always have written 0o600. We also
|
|
31
|
+
// recheck below in safe-mode tests.
|
|
32
|
+
(0, node_fs_1.copyFileSync)(credsPath, backupPath);
|
|
33
|
+
}
|
|
34
|
+
let settled = false;
|
|
35
|
+
const finalize = () => {
|
|
36
|
+
settled = true;
|
|
37
|
+
process.removeListener('SIGINT', sigintHandler);
|
|
38
|
+
};
|
|
39
|
+
const handle = {
|
|
40
|
+
created: liveExists,
|
|
41
|
+
backupPath,
|
|
42
|
+
commit() {
|
|
43
|
+
if (settled)
|
|
44
|
+
return;
|
|
45
|
+
if (backupPath && (0, node_fs_1.existsSync)(backupPath)) {
|
|
46
|
+
try {
|
|
47
|
+
(0, node_fs_1.unlinkSync)(backupPath);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Best-effort. A stray backup file on disk is annoying
|
|
51
|
+
// but not catastrophic — the user can rm it manually
|
|
52
|
+
// or the next backup will create a new one with a
|
|
53
|
+
// different random suffix.
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
finalize();
|
|
57
|
+
},
|
|
58
|
+
restore() {
|
|
59
|
+
if (settled)
|
|
60
|
+
return;
|
|
61
|
+
try {
|
|
62
|
+
if (backupPath && (0, node_fs_1.existsSync)(backupPath)) {
|
|
63
|
+
// Atomic move on the same filesystem. Even if a brand-new
|
|
64
|
+
// credentials file was just written, this rename clobbers
|
|
65
|
+
// it with the backup in a single syscall.
|
|
66
|
+
(0, node_fs_1.renameSync)(backupPath, credsPath);
|
|
67
|
+
}
|
|
68
|
+
else if (!liveExists && (0, node_fs_1.existsSync)(credsPath)) {
|
|
69
|
+
// No backup existed, but a fresh credentials file was
|
|
70
|
+
// written during the failed flow. Remove it so the user
|
|
71
|
+
// ends up in the same "no creds" state they started in.
|
|
72
|
+
(0, node_fs_1.unlinkSync)(credsPath);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Restore failed — usually a file-permissions / disk-full
|
|
77
|
+
// problem. Surface a clear error rather than silently leaving
|
|
78
|
+
// the user with no creds.
|
|
79
|
+
const target = backupPath ?? '(none)';
|
|
80
|
+
throw new Error(`failed to restore credentials backup from ${target}; ` +
|
|
81
|
+
`run \`ritual login\` to re-authenticate, then \`rm -f ${target}\` to clean up`);
|
|
82
|
+
}
|
|
83
|
+
finalize();
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
// SIGINT trap. If the user Ctrl-C's mid-flow, treat it as the
|
|
87
|
+
// failure path. Only fires while the handle is unsettled.
|
|
88
|
+
const sigintHandler = () => {
|
|
89
|
+
if (settled)
|
|
90
|
+
return;
|
|
91
|
+
try {
|
|
92
|
+
handle.restore();
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Surface restore failure to stderr — we're already on the
|
|
96
|
+
// exit path so the error message is the most useful thing
|
|
97
|
+
// to show.
|
|
98
|
+
}
|
|
99
|
+
// Exit with the conventional Ctrl-C status code. Don't let the
|
|
100
|
+
// process linger — if anything else is holding the event loop
|
|
101
|
+
// open (e.g. the loopback HTTP server) it would otherwise.
|
|
102
|
+
process.exit(130);
|
|
103
|
+
};
|
|
104
|
+
process.once('SIGINT', sigintHandler);
|
|
105
|
+
return handle;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Generate a unique backup file path for the given credentials path.
|
|
109
|
+
*
|
|
110
|
+
* Shape: `credentials.json.bak.<pid>.<unix-ms>.<8-byte-random-hex>`.
|
|
111
|
+
* The random suffix prevents collision if a previous run crashed
|
|
112
|
+
* mid-backup. PID and timestamp let ops humans triage orphaned
|
|
113
|
+
* backups.
|
|
114
|
+
*/
|
|
115
|
+
function mintBackupPath(credsPath) {
|
|
116
|
+
const pid = process.pid;
|
|
117
|
+
const ts = Date.now();
|
|
118
|
+
const rand = (0, node_crypto_1.randomBytes)(8).toString('hex');
|
|
119
|
+
return `${credsPath}.bak.${pid}.${ts}.${rand}`;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Path of the advisory lock file used to prevent two concurrent
|
|
123
|
+
* `ritual init` runs from clobbering each other's credential files.
|
|
124
|
+
* Exposed for tests.
|
|
125
|
+
*/
|
|
126
|
+
function getInitLockPath() {
|
|
127
|
+
return (0, node_path_1.join)((0, node_path_1.dirname)((0, config_1.getCredentialsPath)()), 'init.lock');
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Try to acquire the `ritual init` advisory lock. Returns a release
|
|
131
|
+
* function on success. Throws if another `ritual init` is already
|
|
132
|
+
* running.
|
|
133
|
+
*
|
|
134
|
+
* The lock is a plain text file containing the holder's PID. On
|
|
135
|
+
* acquisition we read any existing lock file and check whether the
|
|
136
|
+
* recorded PID is still alive — if it isn't (process crashed, machine
|
|
137
|
+
* rebooted, etc.) the lock is considered stale and we steal it.
|
|
138
|
+
*
|
|
139
|
+
* This is advisory only — it doesn't prevent direct manipulation of
|
|
140
|
+
* the credentials file, just two cooperating `ritual init` runs from
|
|
141
|
+
* racing each other.
|
|
142
|
+
*/
|
|
143
|
+
function acquireInitLock() {
|
|
144
|
+
const lockPath = getInitLockPath();
|
|
145
|
+
if ((0, node_fs_1.existsSync)(lockPath)) {
|
|
146
|
+
let recordedPid;
|
|
147
|
+
try {
|
|
148
|
+
const raw = (0, node_fs_1.statSync)(lockPath).isFile() ? readLockFile(lockPath) : null;
|
|
149
|
+
if (raw)
|
|
150
|
+
recordedPid = Number.parseInt(raw, 10);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// Unreadable lock file — treat as stale.
|
|
154
|
+
}
|
|
155
|
+
const stale = !recordedPid ||
|
|
156
|
+
Number.isNaN(recordedPid) ||
|
|
157
|
+
!isProcessAlive(recordedPid) ||
|
|
158
|
+
recordedPid === process.pid;
|
|
159
|
+
if (!stale) {
|
|
160
|
+
throw new Error(`another \`ritual init\` is already running (pid ${recordedPid}); ` +
|
|
161
|
+
`if you believe that's wrong, remove the stale lock at ${lockPath}`);
|
|
162
|
+
}
|
|
163
|
+
// Stale — fall through and overwrite.
|
|
164
|
+
try {
|
|
165
|
+
(0, node_fs_1.unlinkSync)(lockPath);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
/* ignore — the writeFileSync below will clobber */
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Create-with-mode 0600 so the lock file isn't world-readable.
|
|
172
|
+
const fd = (0, node_fs_1.openSync)(lockPath, 'w', 0o600);
|
|
173
|
+
try {
|
|
174
|
+
(0, node_fs_1.writeSync)(fd, String(process.pid));
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
(0, node_fs_1.closeSync)(fd);
|
|
178
|
+
}
|
|
179
|
+
let released = false;
|
|
180
|
+
return () => {
|
|
181
|
+
if (released)
|
|
182
|
+
return;
|
|
183
|
+
released = true;
|
|
184
|
+
try {
|
|
185
|
+
(0, node_fs_1.unlinkSync)(lockPath);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
/* ignore */
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Cross-platform "is this PID alive" probe. Uses signal-0 which on
|
|
194
|
+
* POSIX checks permission/existence without sending an actual signal.
|
|
195
|
+
* On Windows kill(0) behaves similarly via libuv.
|
|
196
|
+
*/
|
|
197
|
+
function isProcessAlive(pid) {
|
|
198
|
+
try {
|
|
199
|
+
process.kill(pid, 0);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
// ESRCH = no such process. EPERM = process exists but we don't
|
|
204
|
+
// have permission to signal it (i.e. it IS alive).
|
|
205
|
+
const code = err.code;
|
|
206
|
+
if (code === 'EPERM')
|
|
207
|
+
return true;
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function readLockFile(path) {
|
|
212
|
+
try {
|
|
213
|
+
// Defer to writeFileSync's mirror — synchronous read is the
|
|
214
|
+
// simplest correct option here.
|
|
215
|
+
return require('node:fs').readFileSync(path, 'utf-8').trim();
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Convenience helper: combine lock-acquire + backup into one call.
|
|
223
|
+
* The returned `release()` releases the lock; the returned handle is
|
|
224
|
+
* the backup handle.
|
|
225
|
+
*
|
|
226
|
+
* Typical usage:
|
|
227
|
+
*
|
|
228
|
+
* const { release, backup } = beginCredentialSwap();
|
|
229
|
+
* try {
|
|
230
|
+
* // ... attempt fresh sign-in ...
|
|
231
|
+
* backup.commit();
|
|
232
|
+
* } catch (err) {
|
|
233
|
+
* backup.restore();
|
|
234
|
+
* throw err;
|
|
235
|
+
* } finally {
|
|
236
|
+
* release();
|
|
237
|
+
* }
|
|
238
|
+
*/
|
|
239
|
+
function beginCredentialSwap() {
|
|
240
|
+
const release = acquireInitLock();
|
|
241
|
+
let backup;
|
|
242
|
+
try {
|
|
243
|
+
backup = backupCredentials();
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
release();
|
|
247
|
+
throw err;
|
|
248
|
+
}
|
|
249
|
+
return { release, backup };
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Internal test helper. Forcefully writes a file with mode 0o600 via
|
|
253
|
+
* create-with-mode. Used by saveCredentials() in lib/config.ts to
|
|
254
|
+
* replace the chmod-after pattern that has a permission-race window.
|
|
255
|
+
*
|
|
256
|
+
* Exposed from this module so the backup/save code lives next to the
|
|
257
|
+
* credentials-locking code that depends on it.
|
|
258
|
+
*/
|
|
259
|
+
function writeFileMode0600(path, contents) {
|
|
260
|
+
// Open with O_WRONLY | O_CREAT | O_TRUNC (Node's 'w' flag) plus
|
|
261
|
+
// explicit mode. On macOS / Linux the file is created with 0o600
|
|
262
|
+
// from the start, eliminating the chmod-after window.
|
|
263
|
+
const fd = (0, node_fs_1.openSync)(path, 'w', 0o600);
|
|
264
|
+
try {
|
|
265
|
+
(0, node_fs_1.writeSync)(fd, contents);
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
(0, node_fs_1.closeSync)(fd);
|
|
269
|
+
}
|
|
270
|
+
// On a pre-existing file `openSync('w', 0o600)` doesn't change mode.
|
|
271
|
+
// Belt-and-braces: chmod sync afterward. Best-effort, no-op on
|
|
272
|
+
// platforms that don't support chmod.
|
|
273
|
+
try {
|
|
274
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
275
|
+
const { chmodSync } = require('node:fs');
|
|
276
|
+
chmodSync(path, 0o600);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
/* ignore */
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=credentials-backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials-backup.js","sourceRoot":"","sources":["../../src/lib/credentials-backup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.identityFromCreds = identityFromCreds;
|
|
4
|
+
exports.printIdentityBanner = printIdentityBanner;
|
|
5
|
+
const oidc_1 = require("./oidc");
|
|
6
|
+
/**
|
|
7
|
+
* Derive printable identity info from a credentials record.
|
|
8
|
+
*
|
|
9
|
+
* The refresh-token expiry is best-effort — Keycloak's refresh
|
|
10
|
+
* tokens are JWTs and carry an `exp` claim, but the spec doesn't
|
|
11
|
+
* mandate it (some IdPs use opaque refresh tokens). When we can't
|
|
12
|
+
* decode an `exp`, `refreshExpiresAt` is undefined and the banner
|
|
13
|
+
* falls back to "auto-refreshes automatically" without a horizon.
|
|
14
|
+
*/
|
|
15
|
+
function identityFromCreds(creds) {
|
|
16
|
+
const refreshExpiresAt = creds.tokenSet.refresh_token
|
|
17
|
+
? (0, oidc_1.decodeJwtPayloadUnsafe)(creds.tokenSet.refresh_token)?.exp
|
|
18
|
+
: undefined;
|
|
19
|
+
return {
|
|
20
|
+
email: creds.user?.email,
|
|
21
|
+
sub: creds.user?.sub,
|
|
22
|
+
issuer: creds.issuer,
|
|
23
|
+
clientId: creds.clientId,
|
|
24
|
+
accessExpiresAt: creds.tokenSet.expires_at,
|
|
25
|
+
refreshExpiresAt,
|
|
26
|
+
canRefresh: !!creds.tokenSet.refresh_token,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Format a future unix-seconds timestamp as a humanish duration. */
|
|
30
|
+
function formatDuration(targetUnixSec, nowUnixSec) {
|
|
31
|
+
const dt = Math.max(0, targetUnixSec - nowUnixSec);
|
|
32
|
+
if (dt < 60)
|
|
33
|
+
return `${dt}s`;
|
|
34
|
+
if (dt < 60 * 60)
|
|
35
|
+
return `${Math.floor(dt / 60)}m`;
|
|
36
|
+
if (dt < 60 * 60 * 24) {
|
|
37
|
+
const h = Math.floor(dt / 3600);
|
|
38
|
+
const m = Math.floor((dt % 3600) / 60);
|
|
39
|
+
return m === 0 ? `${h}h` : `${h}h ${m}m`;
|
|
40
|
+
}
|
|
41
|
+
const d = Math.floor(dt / 86_400);
|
|
42
|
+
const h = Math.floor((dt % 86_400) / 3600);
|
|
43
|
+
return h === 0 ? `${d}d` : `${d}d ${h}h`;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Emit the identity banner. Routes to stderr (human) or stdout (JSON)
|
|
47
|
+
* based on `opts.json`. Safe to call multiple times.
|
|
48
|
+
*/
|
|
49
|
+
function printIdentityBanner(identity, opts = {}) {
|
|
50
|
+
if (opts.json) {
|
|
51
|
+
const payload = {
|
|
52
|
+
email: identity.email ?? null,
|
|
53
|
+
sub: identity.sub ?? null,
|
|
54
|
+
issuer: identity.issuer,
|
|
55
|
+
clientId: identity.clientId,
|
|
56
|
+
accessExpiresAt: identity.accessExpiresAt,
|
|
57
|
+
refreshExpiresAt: identity.refreshExpiresAt ?? null,
|
|
58
|
+
canRefresh: identity.canRefresh,
|
|
59
|
+
};
|
|
60
|
+
process.stdout.write(JSON.stringify(payload) + '\n');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const indent = opts.indent ?? ' ';
|
|
64
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
65
|
+
const accessIn = formatDuration(identity.accessExpiresAt, nowSec);
|
|
66
|
+
const refreshIn = identity.refreshExpiresAt
|
|
67
|
+
? formatDuration(identity.refreshExpiresAt, nowSec)
|
|
68
|
+
: null;
|
|
69
|
+
const lines = [];
|
|
70
|
+
lines.push(`${indent}Signed in as ${identity.email ?? identity.sub ?? '(unknown)'}`);
|
|
71
|
+
lines.push(`${indent} Access token expires in ${accessIn}`);
|
|
72
|
+
if (identity.canRefresh) {
|
|
73
|
+
if (refreshIn) {
|
|
74
|
+
lines.push(`${indent} Refresh token valid for ${refreshIn}; access refreshes automatically`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
lines.push(`${indent} Access refreshes automatically via stored refresh token`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
lines.push(`${indent} No refresh token stored — sign in again when access expires`);
|
|
82
|
+
}
|
|
83
|
+
if (opts.includeSwitchHint !== false) {
|
|
84
|
+
lines.push(`${indent} To switch accounts: ritual init --switch-account`);
|
|
85
|
+
}
|
|
86
|
+
process.stderr.write(lines.join('\n') + '\n');
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=identity-banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-banner.js","sourceRoot":"","sources":["../../src/lib/identity-banner.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/oidc.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.RefreshTokenError = void 0;
|
|
|
4
4
|
exports.performLoginFlow = performLoginFlow;
|
|
5
5
|
exports.refreshTokens = refreshTokens;
|
|
6
6
|
exports.decodeJwtPayloadUnsafe = decodeJwtPayloadUnsafe;
|
|
7
|
+
exports.buildLogoutUrl = buildLogoutUrl;
|
|
7
8
|
const node_crypto_1 = require("node:crypto");
|
|
8
9
|
const node_http_1 = require("node:http");
|
|
9
10
|
const node_url_1 = require("node:url");
|
|
@@ -118,6 +119,17 @@ async function performLoginFlow(cfg, openBrowser, log) {
|
|
|
118
119
|
authorizeUrl.searchParams.set('state', state);
|
|
119
120
|
authorizeUrl.searchParams.set('code_challenge', challenge);
|
|
120
121
|
authorizeUrl.searchParams.set('code_challenge_method', 'S256');
|
|
122
|
+
if (cfg.forceLogin) {
|
|
123
|
+
// OIDC Core §3.1.2.1: `prompt=login` MUST cause the authorization
|
|
124
|
+
// server to reauthenticate the user, even if they already have an
|
|
125
|
+
// active SSO session in the browser. Used by --switch-account so
|
|
126
|
+
// users aren't silently re-signed-in as the previous Keycloak
|
|
127
|
+
// account from the SSO cookie. Brokered IdPs (Google, SAML) are
|
|
128
|
+
// expected to propagate this as `max_age=0` or equivalent — we
|
|
129
|
+
// document that as part of the PR's manual test plan since
|
|
130
|
+
// Keycloak's downstream-prompt behavior depends on broker config.
|
|
131
|
+
authorizeUrl.searchParams.set('prompt', 'login');
|
|
132
|
+
}
|
|
121
133
|
log(`Opening browser to ${cfg.issuer.replace(/^https?:\/\//, '')}…`);
|
|
122
134
|
log(`If the browser doesn't open, visit:\n ${authorizeUrl.toString()}\n`);
|
|
123
135
|
await openBrowser(authorizeUrl.toString()).catch(() => {
|
|
@@ -252,6 +264,49 @@ function cliSuccessFallbackUrl(issuer) {
|
|
|
252
264
|
return 'https://app.ritualapp.cloud/auth/signin?source=cli';
|
|
253
265
|
}
|
|
254
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Build a Keycloak RP-initiated logout URL per OIDC Session
|
|
269
|
+
* Management spec (OpenID Connect RP-Initiated Logout 1.0).
|
|
270
|
+
*
|
|
271
|
+
* Keycloak's endpoint is `<issuer>/protocol/openid-connect/logout`.
|
|
272
|
+
* Accepted parameters (we use a subset):
|
|
273
|
+
*
|
|
274
|
+
* - `id_token_hint` : recommended by spec — lets the OP
|
|
275
|
+
* skip the "are you sure?" confirm
|
|
276
|
+
* page since it can identify the
|
|
277
|
+
* session from the token.
|
|
278
|
+
* - `client_id` : the OIDC client requesting logout.
|
|
279
|
+
* - `post_logout_redirect_uri` : **intentionally omitted**. Keycloak
|
|
280
|
+
* requires registered redirect URIs
|
|
281
|
+
* for this parameter, and we don't
|
|
282
|
+
* want to couple this CLI logout
|
|
283
|
+
* feature to the realm-import path
|
|
284
|
+
* that defines those URIs. Users see
|
|
285
|
+
* Keycloak's default post-logout
|
|
286
|
+
* landing page (which says "you've
|
|
287
|
+
* been signed out") and close the
|
|
288
|
+
* tab.
|
|
289
|
+
* - `state` : not used (no redirect to validate).
|
|
290
|
+
*
|
|
291
|
+
* Limitation we're explicit about in the surrounding `ritual logout
|
|
292
|
+
* --all` copy: this clears the LOCAL credentials + the Keycloak SSO
|
|
293
|
+
* cookie. It does NOT revoke minted Personal Access Tokens, refresh
|
|
294
|
+
* tokens already stored in agent MCP configs, or other server-side
|
|
295
|
+
* session artifacts. Users who want those revoked too should visit
|
|
296
|
+
* the manage-tokens UI surfaced in the message.
|
|
297
|
+
*
|
|
298
|
+
* Spec references:
|
|
299
|
+
* - OpenID Connect RP-Initiated Logout 1.0 §2
|
|
300
|
+
* - Keycloak docs: "Securing Apps · OIDC layers · Logout endpoint"
|
|
301
|
+
*/
|
|
302
|
+
function buildLogoutUrl(args) {
|
|
303
|
+
const url = new node_url_1.URL(`${args.issuer}/protocol/openid-connect/logout`);
|
|
304
|
+
url.searchParams.set('client_id', args.clientId);
|
|
305
|
+
if (args.idToken) {
|
|
306
|
+
url.searchParams.set('id_token_hint', args.idToken);
|
|
307
|
+
}
|
|
308
|
+
return url.toString();
|
|
309
|
+
}
|
|
255
310
|
// ─── small html helpers for the loopback browser response ──────────────────
|
|
256
311
|
function htmlPage(title, body) {
|
|
257
312
|
return `<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml(title)}</title>
|
package/dist/lib/oidc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oidc.js","sourceRoot":"","sources":["../../src/lib/oidc.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"oidc.js","sourceRoot":"","sources":["../../src/lib/oidc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readPackageVersionSafe = readPackageVersionSafe;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
/**
|
|
40
|
+
* Read the CLI package version at runtime so banners + headers
|
|
41
|
+
* always match the installed package. Shared between `src/index.ts`
|
|
42
|
+
* (for `ritual --version`) and the welcome banner header.
|
|
43
|
+
*
|
|
44
|
+
* Looks for `package.json` relative to the running script's
|
|
45
|
+
* directory. After build the CLI ships as `dist/index.js`, so
|
|
46
|
+
* `__dirname/../package.json` resolves correctly both for the
|
|
47
|
+
* published package and for `pnpm dev` (where `tsx` runs from
|
|
48
|
+
* `src/`).
|
|
49
|
+
*
|
|
50
|
+
* Returns `'0.0.0-unknown'` if every candidate path fails — never
|
|
51
|
+
* throws, since this is purely cosmetic UI state.
|
|
52
|
+
*/
|
|
53
|
+
function readPackageVersionSafe() {
|
|
54
|
+
const candidates = [
|
|
55
|
+
path.join(__dirname, '..', '..', 'package.json'),
|
|
56
|
+
path.join(__dirname, '..', 'package.json'),
|
|
57
|
+
path.join(__dirname, '..', '..', '..', 'package.json'),
|
|
58
|
+
];
|
|
59
|
+
for (const candidate of candidates) {
|
|
60
|
+
try {
|
|
61
|
+
const raw = fs.readFileSync(candidate, 'utf-8');
|
|
62
|
+
const parsed = JSON.parse(raw);
|
|
63
|
+
if (typeof parsed.version === 'string' &&
|
|
64
|
+
parsed.version.length > 0 &&
|
|
65
|
+
typeof parsed.name === 'string' &&
|
|
66
|
+
parsed.name.includes('cli')) {
|
|
67
|
+
return parsed.version;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// try next candidate
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return '0.0.0-unknown';
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=package-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-info.js","sourceRoot":"","sources":["../../src/lib/package-info.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBA,wDAuBC;AAxCD,4CAA8B;AAC9B,gDAAkC;AAElC;;;;;;;;;;;;;GAaG;AACH,SAAgB,sBAAsB;IACrC,MAAM,UAAU,GAAG;QAClB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;QAChD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC;KACtD,CAAC;IACF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,CAAC;YACJ,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwC,CAAC;YACtE,IACC,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;gBAClC,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;gBACzB,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;gBAC/B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAC1B,CAAC;gBACF,OAAO,MAAM,CAAC,OAAO,CAAC;YACvB,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,qBAAqB;QACtB,CAAC;IACF,CAAC;IACD,OAAO,eAAe,CAAC;AACxB,CAAC"}
|