workos 0.6.0 → 0.7.1
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 +51 -15
- package/dist/bin.js +151 -0
- package/dist/bin.js.map +1 -1
- package/dist/cli.config.d.ts +1 -0
- package/dist/cli.config.js +1 -0
- package/dist/cli.config.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +1 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/env.d.ts +9 -0
- package/dist/commands/env.js +188 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/organization.d.ts +18 -0
- package/dist/commands/organization.js +142 -0
- package/dist/commands/organization.js.map +1 -0
- package/dist/commands/user.d.ts +19 -0
- package/dist/commands/user.js +128 -0
- package/dist/commands/user.js.map +1 -0
- package/dist/dashboard/components/CompletionView.js +5 -1
- package/dist/dashboard/components/CompletionView.js.map +1 -1
- package/dist/doctor/agent-prompt.d.ts +10 -0
- package/dist/doctor/agent-prompt.js +190 -0
- package/dist/doctor/agent-prompt.js.map +1 -0
- package/dist/doctor/checks/ai-analysis.d.ts +9 -0
- package/dist/doctor/checks/ai-analysis.js +122 -0
- package/dist/doctor/checks/ai-analysis.js.map +1 -0
- package/dist/doctor/checks/auth-patterns.d.ts +2 -0
- package/dist/doctor/checks/auth-patterns.js +530 -0
- package/dist/doctor/checks/auth-patterns.js.map +1 -0
- package/dist/doctor/checks/environment.js +22 -4
- package/dist/doctor/checks/environment.js.map +1 -1
- package/dist/doctor/checks/framework.js +27 -8
- package/dist/doctor/checks/framework.js.map +1 -1
- package/dist/doctor/checks/language.d.ts +6 -0
- package/dist/doctor/checks/language.js +104 -0
- package/dist/doctor/checks/language.js.map +1 -0
- package/dist/doctor/checks/sdk.js +113 -22
- package/dist/doctor/checks/sdk.js.map +1 -1
- package/dist/doctor/index.js +26 -3
- package/dist/doctor/index.js.map +1 -1
- package/dist/doctor/issues.js +22 -1
- package/dist/doctor/issues.js.map +1 -1
- package/dist/doctor/output.js +85 -18
- package/dist/doctor/output.js.map +1 -1
- package/dist/doctor/types.d.ts +38 -0
- package/dist/doctor/types.js.map +1 -1
- package/dist/lib/adapters/cli-adapter.js +4 -14
- package/dist/lib/adapters/cli-adapter.js.map +1 -1
- package/dist/lib/adapters/dashboard-adapter.js +3 -16
- package/dist/lib/adapters/dashboard-adapter.js.map +1 -1
- package/dist/lib/api-key.d.ts +13 -0
- package/dist/lib/api-key.js +26 -0
- package/dist/lib/api-key.js.map +1 -0
- package/dist/lib/config-store.d.ts +27 -0
- package/dist/lib/config-store.js +142 -0
- package/dist/lib/config-store.js.map +1 -0
- package/dist/lib/credential-store.d.ts +4 -0
- package/dist/lib/credential-store.js +47 -11
- package/dist/lib/credential-store.js.map +1 -1
- package/dist/lib/credentials.d.ts +1 -1
- package/dist/lib/credentials.js +1 -1
- package/dist/lib/credentials.js.map +1 -1
- package/dist/lib/run-with-core.js +23 -2
- package/dist/lib/run-with-core.js.map +1 -1
- package/dist/lib/settings.d.ts +1 -0
- package/dist/lib/settings.js.map +1 -1
- package/dist/lib/validation/build-validator.js +0 -1
- package/dist/lib/validation/build-validator.js.map +1 -1
- package/dist/lib/validation/quick-checks.js +0 -1
- package/dist/lib/validation/quick-checks.js.map +1 -1
- package/dist/lib/workos-api.d.ts +30 -0
- package/dist/lib/workos-api.js +69 -0
- package/dist/lib/workos-api.js.map +1 -0
- package/dist/utils/cli-symbols.d.ts +1 -1
- package/dist/utils/lock-art.d.ts +4 -0
- package/dist/utils/lock-art.js +73 -0
- package/dist/utils/lock-art.js.map +1 -0
- package/dist/utils/package-json.d.ts +1 -0
- package/dist/utils/package-json.js +11 -0
- package/dist/utils/package-json.js.map +1 -1
- package/dist/utils/summary-box.d.ts +18 -0
- package/dist/utils/summary-box.js +148 -0
- package/dist/utils/summary-box.js.map +1 -0
- package/dist/utils/table.d.ts +5 -0
- package/dist/utils/table.js +18 -0
- package/dist/utils/table.js.map +1 -0
- package/package.json +1 -1
- package/skills/workos-authkit-nextjs/SKILL.md +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-adapter.js","sourceRoot":"","sources":["../../../src/lib/adapters/dashboard-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"dashboard-adapter.js","sourceRoot":"","sources":["../../../src/lib/adapters/dashboard-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InstallerAdapter, AdapterConfig } from './types.js';\nimport type { InstallerEventEmitter, InstallerEvents } from '../events.js';\nimport { renderCompletionSummary } from '../../utils/summary-box.js';\n\n/**\n * Dashboard adapter that renders wizard events via Ink/React TUI.\n *\n * Wraps the existing Dashboard component and passes the emitter to it.\n * The Dashboard component already handles most event rendering internally.\n */\nexport class DashboardAdapter implements InstallerAdapter {\n readonly emitter: InstallerEventEmitter;\n private sendEvent: AdapterConfig['sendEvent'];\n private cleanup: (() => void) | null = null;\n private isStarted = false;\n private completionData: { success: boolean; summary?: string } | null = null;\n\n constructor(config: AdapterConfig) {\n this.emitter = config.emitter;\n this.sendEvent = config.sendEvent;\n }\n\n async start(): Promise<void> {\n if (this.isStarted) return;\n this.isStarted = true;\n\n // Dynamic imports to avoid loading Ink when not needed\n const { render } = await import('ink');\n const { createElement } = await import('react');\n const { Dashboard } = await import('../../dashboard/components/Dashboard.js');\n\n // Enter fullscreen (alternate screen buffer)\n process.stdout.write('\\x1b[?1049h'); // Enter alternate screen\n process.stdout.write('\\x1b[2J'); // Clear entire screen\n process.stdout.write('\\x1b[H'); // Move cursor to home\n process.stdout.write('\\x1b[?25l'); // Hide cursor\n\n // Render the Dashboard component with emitter\n const instance = render(createElement(Dashboard, { emitter: this.emitter }));\n\n // Setup cleanup function\n this.cleanup = () => {\n instance.unmount();\n process.stdout.write('\\x1b[?25h'); // Show cursor\n process.stdout.write('\\x1b[?1049l'); // Exit alternate screen\n };\n\n // Wire up Dashboard responses back to the machine\n // The Dashboard component emits these when user interacts\n this.emitter.on('confirm:response', this.handleConfirmResponse);\n this.emitter.on('credentials:response', this.handleCredentialsResponse);\n\n // Track completion for post-exit summary\n this.emitter.on('complete', this.handleComplete);\n }\n\n /**\n * Capture completion data for display after exit.\n */\n private handleComplete = ({ success, summary }: InstallerEvents['complete']): void => {\n this.completionData = { success, summary };\n };\n\n async stop(): Promise<void> {\n if (!this.isStarted) return;\n\n // Unsubscribe from events\n this.emitter.off('confirm:response', this.handleConfirmResponse);\n this.emitter.off('credentials:response', this.handleCredentialsResponse);\n this.emitter.off('complete', this.handleComplete);\n\n // Run cleanup (unmount Ink, exit fullscreen)\n this.cleanup?.();\n this.cleanup = null;\n\n if (this.completionData) {\n console.log();\n console.log(renderCompletionSummary(this.completionData.success, this.completionData.summary));\n console.log();\n }\n\n this.isStarted = false;\n }\n\n /**\n * Handle confirm dialog responses from Dashboard.\n */\n private handleConfirmResponse = ({ id, confirmed }: { id: string; confirmed: boolean }): void => {\n if (id === 'git-status') {\n this.sendEvent({ type: confirmed ? 'GIT_CONFIRMED' : 'GIT_CANCELLED' });\n } else if (id === 'env-scan') {\n this.sendEvent({ type: confirmed ? 'ENV_SCAN_APPROVED' : 'ENV_SCAN_DECLINED' });\n } else if (id === 'branch-check') {\n // For dashboard, confirmed=true means create branch, false means continue on current\n this.sendEvent({ type: confirmed ? 'BRANCH_CREATE' : 'BRANCH_CONTINUE' });\n } else if (id === 'commit') {\n this.sendEvent({ type: confirmed ? 'COMMIT_APPROVED' : 'COMMIT_DECLINED' });\n } else if (id === 'pr') {\n this.sendEvent({ type: confirmed ? 'PR_APPROVED' : 'PR_DECLINED' });\n }\n };\n\n /**\n * Handle credentials form submission from Dashboard.\n */\n private handleCredentialsResponse = ({ apiKey, clientId }: { apiKey: string; clientId: string }): void => {\n this.sendEvent({ type: 'CREDENTIALS_SUBMITTED', apiKey, clientId });\n };\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API key resolution for management commands.
|
|
3
|
+
*
|
|
4
|
+
* Priority chain:
|
|
5
|
+
* 1. WORKOS_API_KEY environment variable
|
|
6
|
+
* 2. --api-key flag
|
|
7
|
+
* 3. Active environment's stored API key
|
|
8
|
+
*/
|
|
9
|
+
export interface ApiKeyOptions {
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function resolveApiKey(options?: ApiKeyOptions): string;
|
|
13
|
+
export declare function resolveApiBaseUrl(): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API key resolution for management commands.
|
|
3
|
+
*
|
|
4
|
+
* Priority chain:
|
|
5
|
+
* 1. WORKOS_API_KEY environment variable
|
|
6
|
+
* 2. --api-key flag
|
|
7
|
+
* 3. Active environment's stored API key
|
|
8
|
+
*/
|
|
9
|
+
import { getActiveEnvironment } from './config-store.js';
|
|
10
|
+
const DEFAULT_BASE_URL = 'https://api.workos.com';
|
|
11
|
+
export function resolveApiKey(options) {
|
|
12
|
+
const envVar = process.env.WORKOS_API_KEY;
|
|
13
|
+
if (envVar)
|
|
14
|
+
return envVar;
|
|
15
|
+
if (options?.apiKey)
|
|
16
|
+
return options.apiKey;
|
|
17
|
+
const activeEnv = getActiveEnvironment();
|
|
18
|
+
if (activeEnv?.apiKey)
|
|
19
|
+
return activeEnv.apiKey;
|
|
20
|
+
throw new Error('No API key configured. Run `workos env add` to configure an environment, or set WORKOS_API_KEY.');
|
|
21
|
+
}
|
|
22
|
+
export function resolveApiBaseUrl() {
|
|
23
|
+
const activeEnv = getActiveEnvironment();
|
|
24
|
+
return activeEnv?.endpoint || DEFAULT_BASE_URL;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=api-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key.js","sourceRoot":"","sources":["../../src/lib/api-key.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,gBAAgB,GAAG,wBAAwB,CAAC;AAMlD,MAAM,UAAU,aAAa,CAAC,OAAuB;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC1C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,IAAI,OAAO,EAAE,MAAM;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC;IAE3C,MAAM,SAAS,GAAG,oBAAoB,EAAE,CAAC;IACzC,IAAI,SAAS,EAAE,MAAM;QAAE,OAAO,SAAS,CAAC,MAAM,CAAC;IAE/C,MAAM,IAAI,KAAK,CAAC,iGAAiG,CAAC,CAAC;AACrH,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,MAAM,SAAS,GAAG,oBAAoB,EAAE,CAAC;IACzC,OAAO,SAAS,EAAE,QAAQ,IAAI,gBAAgB,CAAC;AACjD,CAAC","sourcesContent":["/**\n * API key resolution for management commands.\n *\n * Priority chain:\n * 1. WORKOS_API_KEY environment variable\n * 2. --api-key flag\n * 3. Active environment's stored API key\n */\n\nimport { getActiveEnvironment } from './config-store.js';\n\nconst DEFAULT_BASE_URL = 'https://api.workos.com';\n\nexport interface ApiKeyOptions {\n apiKey?: string;\n}\n\nexport function resolveApiKey(options?: ApiKeyOptions): string {\n const envVar = process.env.WORKOS_API_KEY;\n if (envVar) return envVar;\n\n if (options?.apiKey) return options.apiKey;\n\n const activeEnv = getActiveEnvironment();\n if (activeEnv?.apiKey) return activeEnv.apiKey;\n\n throw new Error('No API key configured. Run `workos env add` to configure an environment, or set WORKOS_API_KEY.');\n}\n\nexport function resolveApiBaseUrl(): string {\n const activeEnv = getActiveEnvironment();\n return activeEnv?.endpoint || DEFAULT_BASE_URL;\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI config storage abstraction with keyring support and file fallback.
|
|
3
|
+
*
|
|
4
|
+
* Stores environment configurations (names, API keys, endpoints) separately
|
|
5
|
+
* from OAuth credentials. Uses a second keyring entry under the same service.
|
|
6
|
+
*
|
|
7
|
+
* Storage priority:
|
|
8
|
+
* 1. If insecure storage forced: use file only
|
|
9
|
+
* 2. Try keyring, fall back to file with warning if unavailable
|
|
10
|
+
*/
|
|
11
|
+
export interface EnvironmentConfig {
|
|
12
|
+
name: string;
|
|
13
|
+
type: 'production' | 'sandbox';
|
|
14
|
+
apiKey: string;
|
|
15
|
+
clientId?: string;
|
|
16
|
+
endpoint?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface CliConfig {
|
|
19
|
+
activeEnvironment?: string;
|
|
20
|
+
environments: Record<string, EnvironmentConfig>;
|
|
21
|
+
}
|
|
22
|
+
export declare function setInsecureConfigStorage(value: boolean): void;
|
|
23
|
+
export declare function getConfig(): CliConfig | null;
|
|
24
|
+
export declare function saveConfig(config: CliConfig): void;
|
|
25
|
+
export declare function clearConfig(): void;
|
|
26
|
+
export declare function getActiveEnvironment(): EnvironmentConfig | null;
|
|
27
|
+
export declare function getConfigPath(): string;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI config storage abstraction with keyring support and file fallback.
|
|
3
|
+
*
|
|
4
|
+
* Stores environment configurations (names, API keys, endpoints) separately
|
|
5
|
+
* from OAuth credentials. Uses a second keyring entry under the same service.
|
|
6
|
+
*
|
|
7
|
+
* Storage priority:
|
|
8
|
+
* 1. If insecure storage forced: use file only
|
|
9
|
+
* 2. Try keyring, fall back to file with warning if unavailable
|
|
10
|
+
*/
|
|
11
|
+
import { Entry } from '@napi-rs/keyring';
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import os from 'node:os';
|
|
15
|
+
import { logWarn } from '../utils/debug.js';
|
|
16
|
+
const SERVICE_NAME = 'workos-cli';
|
|
17
|
+
const ACCOUNT_NAME = 'config';
|
|
18
|
+
let fallbackWarningShown = false;
|
|
19
|
+
let forceInsecureStorage = false;
|
|
20
|
+
export function setInsecureConfigStorage(value) {
|
|
21
|
+
forceInsecureStorage = value;
|
|
22
|
+
}
|
|
23
|
+
function getConfigDir() {
|
|
24
|
+
return path.join(os.homedir(), '.workos');
|
|
25
|
+
}
|
|
26
|
+
function getConfigFilePath() {
|
|
27
|
+
return path.join(getConfigDir(), 'config.json');
|
|
28
|
+
}
|
|
29
|
+
function fileExists() {
|
|
30
|
+
return fs.existsSync(getConfigFilePath());
|
|
31
|
+
}
|
|
32
|
+
function readFromFile() {
|
|
33
|
+
if (!fileExists())
|
|
34
|
+
return null;
|
|
35
|
+
try {
|
|
36
|
+
const content = fs.readFileSync(getConfigFilePath(), 'utf-8');
|
|
37
|
+
return JSON.parse(content);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
logWarn('Failed to read config file:', error);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function writeToFile(config) {
|
|
45
|
+
const dir = getConfigDir();
|
|
46
|
+
if (!fs.existsSync(dir)) {
|
|
47
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
48
|
+
}
|
|
49
|
+
fs.writeFileSync(getConfigFilePath(), JSON.stringify(config, null, 2), {
|
|
50
|
+
mode: 0o600,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function deleteFile() {
|
|
54
|
+
if (fileExists()) {
|
|
55
|
+
fs.unlinkSync(getConfigFilePath());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function getKeyringEntry() {
|
|
59
|
+
return new Entry(SERVICE_NAME, ACCOUNT_NAME);
|
|
60
|
+
}
|
|
61
|
+
function readFromKeyring() {
|
|
62
|
+
try {
|
|
63
|
+
const entry = getKeyringEntry();
|
|
64
|
+
const data = entry.getPassword();
|
|
65
|
+
if (!data)
|
|
66
|
+
return null;
|
|
67
|
+
return JSON.parse(data);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
logWarn('Failed to read config from keyring:', error);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function writeToKeyring(config) {
|
|
75
|
+
try {
|
|
76
|
+
const entry = getKeyringEntry();
|
|
77
|
+
entry.setPassword(JSON.stringify(config));
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
logWarn('Failed to write config to keyring:', error);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function deleteFromKeyring() {
|
|
86
|
+
try {
|
|
87
|
+
const entry = getKeyringEntry();
|
|
88
|
+
entry.deletePassword();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
92
|
+
if (!msg.includes('not found') && !msg.includes('No such')) {
|
|
93
|
+
logWarn('Failed to delete config from keyring:', error);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function showFallbackWarning() {
|
|
98
|
+
if (fallbackWarningShown || forceInsecureStorage)
|
|
99
|
+
return;
|
|
100
|
+
fallbackWarningShown = true;
|
|
101
|
+
logWarn('Unable to store config in system keyring. Using file storage.', 'Config saved to ~/.workos/config.json', 'Use --insecure-storage to suppress this warning.');
|
|
102
|
+
}
|
|
103
|
+
export function getConfig() {
|
|
104
|
+
if (forceInsecureStorage)
|
|
105
|
+
return readFromFile();
|
|
106
|
+
const keyringConfig = readFromKeyring();
|
|
107
|
+
if (keyringConfig)
|
|
108
|
+
return keyringConfig;
|
|
109
|
+
const fileConfig = readFromFile();
|
|
110
|
+
if (fileConfig) {
|
|
111
|
+
// Migrate file config to keyring if possible
|
|
112
|
+
if (writeToKeyring(fileConfig))
|
|
113
|
+
deleteFile();
|
|
114
|
+
return fileConfig;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
export function saveConfig(config) {
|
|
119
|
+
if (forceInsecureStorage)
|
|
120
|
+
return writeToFile(config);
|
|
121
|
+
if (writeToKeyring(config)) {
|
|
122
|
+
deleteFile();
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
showFallbackWarning();
|
|
126
|
+
writeToFile(config);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export function clearConfig() {
|
|
130
|
+
deleteFromKeyring();
|
|
131
|
+
deleteFile();
|
|
132
|
+
}
|
|
133
|
+
export function getActiveEnvironment() {
|
|
134
|
+
const config = getConfig();
|
|
135
|
+
if (!config?.activeEnvironment)
|
|
136
|
+
return null;
|
|
137
|
+
return config.environments[config.activeEnvironment] ?? null;
|
|
138
|
+
}
|
|
139
|
+
export function getConfigPath() {
|
|
140
|
+
return getConfigFilePath();
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=config-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-store.js","sourceRoot":"","sources":["../../src/lib/config-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CLI config storage abstraction with keyring support and file fallback.\n *\n * Stores environment configurations (names, API keys, endpoints) separately\n * from OAuth credentials. Uses a second keyring entry under the same service.\n *\n * Storage priority:\n * 1. If insecure storage forced: use file only\n * 2. Try keyring, fall back to file with warning if unavailable\n */\n\nimport { Entry } from '@napi-rs/keyring';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { logWarn } from '../utils/debug.js';\n\nexport interface EnvironmentConfig {\n name: string;\n type: 'production' | 'sandbox';\n apiKey: string;\n clientId?: string;\n endpoint?: string;\n}\n\nexport interface CliConfig {\n activeEnvironment?: string;\n environments: Record<string, EnvironmentConfig>;\n}\n\nconst SERVICE_NAME = 'workos-cli';\nconst ACCOUNT_NAME = 'config';\n\nlet fallbackWarningShown = false;\nlet forceInsecureStorage = false;\n\nexport function setInsecureConfigStorage(value: boolean): void {\n forceInsecureStorage = value;\n}\n\nfunction getConfigDir(): string {\n return path.join(os.homedir(), '.workos');\n}\n\nfunction getConfigFilePath(): string {\n return path.join(getConfigDir(), 'config.json');\n}\n\nfunction fileExists(): boolean {\n return fs.existsSync(getConfigFilePath());\n}\n\nfunction readFromFile(): CliConfig | null {\n if (!fileExists()) return null;\n try {\n const content = fs.readFileSync(getConfigFilePath(), 'utf-8');\n return JSON.parse(content);\n } catch (error) {\n logWarn('Failed to read config file:', error);\n return null;\n }\n}\n\nfunction writeToFile(config: CliConfig): void {\n const dir = getConfigDir();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n fs.writeFileSync(getConfigFilePath(), JSON.stringify(config, null, 2), {\n mode: 0o600,\n });\n}\n\nfunction deleteFile(): void {\n if (fileExists()) {\n fs.unlinkSync(getConfigFilePath());\n }\n}\n\nfunction getKeyringEntry(): Entry {\n return new Entry(SERVICE_NAME, ACCOUNT_NAME);\n}\n\nfunction readFromKeyring(): CliConfig | null {\n try {\n const entry = getKeyringEntry();\n const data = entry.getPassword();\n if (!data) return null;\n return JSON.parse(data);\n } catch (error) {\n logWarn('Failed to read config from keyring:', error);\n return null;\n }\n}\n\nfunction writeToKeyring(config: CliConfig): boolean {\n try {\n const entry = getKeyringEntry();\n entry.setPassword(JSON.stringify(config));\n return true;\n } catch (error) {\n logWarn('Failed to write config to keyring:', error);\n return false;\n }\n}\n\nfunction deleteFromKeyring(): void {\n try {\n const entry = getKeyringEntry();\n entry.deletePassword();\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n if (!msg.includes('not found') && !msg.includes('No such')) {\n logWarn('Failed to delete config from keyring:', error);\n }\n }\n}\n\nfunction showFallbackWarning(): void {\n if (fallbackWarningShown || forceInsecureStorage) return;\n fallbackWarningShown = true;\n logWarn(\n 'Unable to store config in system keyring. Using file storage.',\n 'Config saved to ~/.workos/config.json',\n 'Use --insecure-storage to suppress this warning.',\n );\n}\n\nexport function getConfig(): CliConfig | null {\n if (forceInsecureStorage) return readFromFile();\n\n const keyringConfig = readFromKeyring();\n if (keyringConfig) return keyringConfig;\n\n const fileConfig = readFromFile();\n if (fileConfig) {\n // Migrate file config to keyring if possible\n if (writeToKeyring(fileConfig)) deleteFile();\n return fileConfig;\n }\n\n return null;\n}\n\nexport function saveConfig(config: CliConfig): void {\n if (forceInsecureStorage) return writeToFile(config);\n\n if (writeToKeyring(config)) {\n deleteFile();\n } else {\n showFallbackWarning();\n writeToFile(config);\n }\n}\n\nexport function clearConfig(): void {\n deleteFromKeyring();\n deleteFile();\n}\n\nexport function getActiveEnvironment(): EnvironmentConfig | null {\n const config = getConfig();\n if (!config?.activeEnvironment) return null;\n return config.environments[config.activeEnvironment] ?? null;\n}\n\nexport function getConfigPath(): string {\n return getConfigFilePath();\n}\n"]}
|
|
@@ -25,4 +25,8 @@ export declare function getCredentials(): Credentials | null;
|
|
|
25
25
|
export declare function saveCredentials(creds: Credentials): void;
|
|
26
26
|
export declare function clearCredentials(): void;
|
|
27
27
|
export declare function updateTokens(accessToken: string, expiresAt: number, refreshToken?: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Diagnostic info about credential storage state — for debugging auth failures.
|
|
30
|
+
*/
|
|
31
|
+
export declare function diagnoseCredentials(): string[];
|
|
28
32
|
export { getCredentialsPath };
|
|
@@ -59,12 +59,15 @@ function readFromKeyring() {
|
|
|
59
59
|
try {
|
|
60
60
|
const entry = getKeyringEntry();
|
|
61
61
|
const data = entry.getPassword();
|
|
62
|
-
if (!data)
|
|
62
|
+
if (!data) {
|
|
63
|
+
logWarn('[credential-store] keyring: entry exists but data is null/empty');
|
|
63
64
|
return null;
|
|
65
|
+
}
|
|
64
66
|
return JSON.parse(data);
|
|
65
67
|
}
|
|
66
68
|
catch (error) {
|
|
67
|
-
|
|
69
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
70
|
+
logWarn(`[credential-store] keyring read failed: ${msg}`);
|
|
68
71
|
return null;
|
|
69
72
|
}
|
|
70
73
|
}
|
|
@@ -75,7 +78,8 @@ function writeToKeyring(creds) {
|
|
|
75
78
|
return true;
|
|
76
79
|
}
|
|
77
80
|
catch (error) {
|
|
78
|
-
|
|
81
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
82
|
+
logWarn(`[credential-store] keyring write failed: ${msg}`);
|
|
79
83
|
return false;
|
|
80
84
|
}
|
|
81
85
|
}
|
|
@@ -111,9 +115,7 @@ export function getCredentials() {
|
|
|
111
115
|
return keyringCreds;
|
|
112
116
|
const fileCreds = readFromFile();
|
|
113
117
|
if (fileCreds) {
|
|
114
|
-
|
|
115
|
-
if (writeToKeyring(fileCreds))
|
|
116
|
-
deleteFile();
|
|
118
|
+
writeToKeyring(fileCreds);
|
|
117
119
|
return fileCreds;
|
|
118
120
|
}
|
|
119
121
|
return null;
|
|
@@ -121,12 +123,9 @@ export function getCredentials() {
|
|
|
121
123
|
export function saveCredentials(creds) {
|
|
122
124
|
if (forceInsecureStorage)
|
|
123
125
|
return writeToFile(creds);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
126
|
+
writeToFile(creds);
|
|
127
|
+
if (!writeToKeyring(creds)) {
|
|
128
128
|
showFallbackWarning();
|
|
129
|
-
writeToFile(creds);
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
131
|
export function clearCredentials() {
|
|
@@ -146,5 +145,42 @@ export function updateTokens(accessToken, expiresAt, refreshToken) {
|
|
|
146
145
|
};
|
|
147
146
|
saveCredentials(updated);
|
|
148
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Diagnostic info about credential storage state — for debugging auth failures.
|
|
150
|
+
*/
|
|
151
|
+
export function diagnoseCredentials() {
|
|
152
|
+
const lines = [];
|
|
153
|
+
const filePath = getCredentialsPath();
|
|
154
|
+
const filePresent = fileExists();
|
|
155
|
+
lines.push(`file: ${filePath} (exists=${filePresent})`);
|
|
156
|
+
if (filePresent) {
|
|
157
|
+
try {
|
|
158
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
159
|
+
const parsed = JSON.parse(content);
|
|
160
|
+
const expired = parsed.expiresAt ? Date.now() >= parsed.expiresAt : 'unknown';
|
|
161
|
+
lines.push(`file creds: userId=${parsed.userId ?? 'missing'}, expired=${expired}, hasRefreshToken=${!!parsed.refreshToken}`);
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
lines.push(`file creds: parse error — ${e instanceof Error ? e.message : String(e)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const entry = getKeyringEntry();
|
|
169
|
+
const data = entry.getPassword();
|
|
170
|
+
if (data) {
|
|
171
|
+
const parsed = JSON.parse(data);
|
|
172
|
+
const expired = parsed.expiresAt ? Date.now() >= parsed.expiresAt : 'unknown';
|
|
173
|
+
lines.push(`keyring: found, userId=${parsed.userId ?? 'missing'}, expired=${expired}, hasRefreshToken=${!!parsed.refreshToken}`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
lines.push('keyring: empty (getPassword returned null)');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (e) {
|
|
180
|
+
lines.push(`keyring: error — ${e instanceof Error ? e.message : String(e)}`);
|
|
181
|
+
}
|
|
182
|
+
lines.push(`insecureStorage=${forceInsecureStorage}`);
|
|
183
|
+
return lines;
|
|
184
|
+
}
|
|
149
185
|
export { getCredentialsPath };
|
|
150
186
|
//# sourceMappingURL=credential-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-store.js","sourceRoot":"","sources":["../../src/lib/credential-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAiB5C,MAAM,YAAY,GAAG,YAAY,CAAC;AAClC,MAAM,YAAY,GAAG,aAAa,CAAC;AAEnC,IAAI,oBAAoB,GAAG,KAAK,CAAC;AACjC,IAAI,oBAAoB,GAAG,KAAK,CAAC;AAEjC,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,oBAAoB,GAAG,KAAK,CAAC;AAC/B,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,kBAAkB,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,EAAE,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,YAAY;IACnB,IAAI,CAAC,UAAU,EAAE;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,kBAAkB,EAAE,EAAE,OAAO,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,KAAkB;IACrC,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,EAAE,CAAC,aAAa,CAAC,kBAAkB,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QACrE,IAAI,EAAE,KAAK;KACZ,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU;IACjB,IAAI,UAAU,EAAE,EAAE,CAAC;QACjB,EAAE,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,IAAI,KAAK,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,eAAe;IACtB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,KAAkB;IACxC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;QAChC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB;IACxB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;QAChC,KAAK,CAAC,cAAc,EAAE,CAAC;IACzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3D,OAAO,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB;IAC1B,IAAI,oBAAoB,IAAI,oBAAoB;QAAE,OAAO;IACzD,oBAAoB,GAAG,IAAI,CAAC;IAC5B,OAAO,CACL,oEAAoE,EACpE,iDAAiD,EACjD,kDAAkD,CACnD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,IAAI,oBAAoB,EAAE,CAAC;QACzB,OAAO,UAAU,EAAE,CAAC;IACtB,CAAC;IACD,OAAO,eAAe,EAAE,KAAK,IAAI,IAAI,UAAU,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,IAAI,oBAAoB;QAAE,OAAO,YAAY,EAAE,CAAC;IAEhD,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;IACvC,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IAEtC,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,IAAI,SAAS,EAAE,CAAC;QACd,4CAA4C;QAC5C,IAAI,cAAc,CAAC,SAAS,CAAC;YAAE,UAAU,EAAE,CAAC;QAC5C,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,IAAI,oBAAoB;QAAE,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;IAEpD,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,UAAU,EAAE,CAAC;IACf,CAAC;SAAM,CAAC;QACN,mBAAmB,EAAE,CAAC;QACtB,WAAW,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,iBAAiB,EAAE,CAAC;IACpB,UAAU,EAAE,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,WAAmB,EAAE,SAAiB,EAAE,YAAqB;IACxF,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,OAAO,GAAgB;QAC3B,GAAG,KAAK;QACR,WAAW;QACX,SAAS;QACT,GAAG,CAAC,YAAY,IAAI,EAAE,YAAY,EAAE,CAAC;KACtC,CAAC;IAEF,eAAe,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC","sourcesContent":["/**\n * Credential storage abstraction with keyring support and file fallback.\n *\n * Storage priority:\n * 1. If --insecure-storage: use file only\n * 2. Try keyring, fall back to file with warning if unavailable\n */\n\nimport { Entry } from '@napi-rs/keyring';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { logWarn } from '../utils/debug.js';\n\nexport interface StagingCache {\n clientId: string;\n apiKey: string;\n fetchedAt: number;\n}\n\nexport interface Credentials {\n accessToken: string;\n expiresAt: number;\n userId: string;\n email?: string;\n staging?: StagingCache;\n refreshToken?: string;\n}\n\nconst SERVICE_NAME = 'workos-cli';\nconst ACCOUNT_NAME = 'credentials';\n\nlet fallbackWarningShown = false;\nlet forceInsecureStorage = false;\n\nexport function setInsecureStorage(value: boolean): void {\n forceInsecureStorage = value;\n}\n\nfunction getCredentialsDir(): string {\n return path.join(os.homedir(), '.workos');\n}\n\nfunction getCredentialsPath(): string {\n return path.join(getCredentialsDir(), 'credentials.json');\n}\n\nfunction fileExists(): boolean {\n return fs.existsSync(getCredentialsPath());\n}\n\nfunction readFromFile(): Credentials | null {\n if (!fileExists()) return null;\n try {\n const content = fs.readFileSync(getCredentialsPath(), 'utf-8');\n return JSON.parse(content);\n } catch (error) {\n logWarn('Failed to read credentials file:', error);\n return null;\n }\n}\n\nfunction writeToFile(creds: Credentials): void {\n const dir = getCredentialsDir();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n fs.writeFileSync(getCredentialsPath(), JSON.stringify(creds, null, 2), {\n mode: 0o600,\n });\n}\n\nfunction deleteFile(): void {\n if (fileExists()) {\n fs.unlinkSync(getCredentialsPath());\n }\n}\n\nfunction getKeyringEntry(): Entry {\n return new Entry(SERVICE_NAME, ACCOUNT_NAME);\n}\n\nfunction readFromKeyring(): Credentials | null {\n try {\n const entry = getKeyringEntry();\n const data = entry.getPassword();\n if (!data) return null;\n return JSON.parse(data);\n } catch (error) {\n logWarn('Failed to read from keyring:', error);\n return null;\n }\n}\n\nfunction writeToKeyring(creds: Credentials): boolean {\n try {\n const entry = getKeyringEntry();\n entry.setPassword(JSON.stringify(creds));\n return true;\n } catch (error) {\n logWarn('Failed to write to keyring:', error);\n return false;\n }\n}\n\nfunction deleteFromKeyring(): void {\n try {\n const entry = getKeyringEntry();\n entry.deletePassword();\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n if (!msg.includes('not found') && !msg.includes('No such')) {\n logWarn('Failed to delete from keyring:', error);\n }\n }\n}\n\nfunction showFallbackWarning(): void {\n if (fallbackWarningShown || forceInsecureStorage) return;\n fallbackWarningShown = true;\n logWarn(\n 'Unable to store credentials in system keyring. Using file storage.',\n 'Credentials saved to ~/.workos/credentials.json',\n 'Use --insecure-storage to suppress this warning.',\n );\n}\n\nexport function hasCredentials(): boolean {\n if (forceInsecureStorage) {\n return fileExists();\n }\n return readFromKeyring() !== null || fileExists();\n}\n\nexport function getCredentials(): Credentials | null {\n if (forceInsecureStorage) return readFromFile();\n\n const keyringCreds = readFromKeyring();\n if (keyringCreds) return keyringCreds;\n\n const fileCreds = readFromFile();\n if (fileCreds) {\n // Migrate file creds to keyring if possible\n if (writeToKeyring(fileCreds)) deleteFile();\n return fileCreds;\n }\n\n return null;\n}\n\nexport function saveCredentials(creds: Credentials): void {\n if (forceInsecureStorage) return writeToFile(creds);\n\n if (writeToKeyring(creds)) {\n deleteFile();\n } else {\n showFallbackWarning();\n writeToFile(creds);\n }\n}\n\nexport function clearCredentials(): void {\n deleteFromKeyring();\n deleteFile();\n}\n\nexport function updateTokens(accessToken: string, expiresAt: number, refreshToken?: string): void {\n const creds = getCredentials();\n if (!creds) {\n throw new Error('No existing credentials to update');\n }\n\n const updated: Credentials = {\n ...creds,\n accessToken,\n expiresAt,\n ...(refreshToken && { refreshToken }),\n };\n\n saveCredentials(updated);\n}\n\nexport { getCredentialsPath };\n"]}
|
|
1
|
+
{"version":3,"file":"credential-store.js","sourceRoot":"","sources":["../../src/lib/credential-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Credential storage abstraction with keyring support and file fallback.\n *\n * Storage priority:\n * 1. If --insecure-storage: use file only\n * 2. Try keyring, fall back to file with warning if unavailable\n */\n\nimport { Entry } from '@napi-rs/keyring';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { logWarn } from '../utils/debug.js';\n\nexport interface StagingCache {\n clientId: string;\n apiKey: string;\n fetchedAt: number;\n}\n\nexport interface Credentials {\n accessToken: string;\n expiresAt: number;\n userId: string;\n email?: string;\n staging?: StagingCache;\n refreshToken?: string;\n}\n\nconst SERVICE_NAME = 'workos-cli';\nconst ACCOUNT_NAME = 'credentials';\n\nlet fallbackWarningShown = false;\nlet forceInsecureStorage = false;\n\nexport function setInsecureStorage(value: boolean): void {\n forceInsecureStorage = value;\n}\n\nfunction getCredentialsDir(): string {\n return path.join(os.homedir(), '.workos');\n}\n\nfunction getCredentialsPath(): string {\n return path.join(getCredentialsDir(), 'credentials.json');\n}\n\nfunction fileExists(): boolean {\n return fs.existsSync(getCredentialsPath());\n}\n\nfunction readFromFile(): Credentials | null {\n if (!fileExists()) return null;\n try {\n const content = fs.readFileSync(getCredentialsPath(), 'utf-8');\n return JSON.parse(content);\n } catch (error) {\n logWarn('Failed to read credentials file:', error);\n return null;\n }\n}\n\nfunction writeToFile(creds: Credentials): void {\n const dir = getCredentialsDir();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n fs.writeFileSync(getCredentialsPath(), JSON.stringify(creds, null, 2), {\n mode: 0o600,\n });\n}\n\nfunction deleteFile(): void {\n if (fileExists()) {\n fs.unlinkSync(getCredentialsPath());\n }\n}\n\nfunction getKeyringEntry(): Entry {\n return new Entry(SERVICE_NAME, ACCOUNT_NAME);\n}\n\nfunction readFromKeyring(): Credentials | null {\n try {\n const entry = getKeyringEntry();\n const data = entry.getPassword();\n if (!data) {\n logWarn('[credential-store] keyring: entry exists but data is null/empty');\n return null;\n }\n return JSON.parse(data);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n logWarn(`[credential-store] keyring read failed: ${msg}`);\n return null;\n }\n}\n\nfunction writeToKeyring(creds: Credentials): boolean {\n try {\n const entry = getKeyringEntry();\n entry.setPassword(JSON.stringify(creds));\n return true;\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n logWarn(`[credential-store] keyring write failed: ${msg}`);\n return false;\n }\n}\n\nfunction deleteFromKeyring(): void {\n try {\n const entry = getKeyringEntry();\n entry.deletePassword();\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n if (!msg.includes('not found') && !msg.includes('No such')) {\n logWarn('Failed to delete from keyring:', error);\n }\n }\n}\n\nfunction showFallbackWarning(): void {\n if (fallbackWarningShown || forceInsecureStorage) return;\n fallbackWarningShown = true;\n logWarn(\n 'Unable to store credentials in system keyring. Using file storage.',\n 'Credentials saved to ~/.workos/credentials.json',\n 'Use --insecure-storage to suppress this warning.',\n );\n}\n\nexport function hasCredentials(): boolean {\n if (forceInsecureStorage) {\n return fileExists();\n }\n return readFromKeyring() !== null || fileExists();\n}\n\nexport function getCredentials(): Credentials | null {\n if (forceInsecureStorage) return readFromFile();\n\n const keyringCreds = readFromKeyring();\n if (keyringCreds) return keyringCreds;\n\n const fileCreds = readFromFile();\n if (fileCreds) {\n writeToKeyring(fileCreds);\n return fileCreds;\n }\n\n return null;\n}\n\nexport function saveCredentials(creds: Credentials): void {\n if (forceInsecureStorage) return writeToFile(creds);\n\n writeToFile(creds);\n if (!writeToKeyring(creds)) {\n showFallbackWarning();\n }\n}\n\nexport function clearCredentials(): void {\n deleteFromKeyring();\n deleteFile();\n}\n\nexport function updateTokens(accessToken: string, expiresAt: number, refreshToken?: string): void {\n const creds = getCredentials();\n if (!creds) {\n throw new Error('No existing credentials to update');\n }\n\n const updated: Credentials = {\n ...creds,\n accessToken,\n expiresAt,\n ...(refreshToken && { refreshToken }),\n };\n\n saveCredentials(updated);\n}\n\n/**\n * Diagnostic info about credential storage state — for debugging auth failures.\n */\nexport function diagnoseCredentials(): string[] {\n const lines: string[] = [];\n const filePath = getCredentialsPath();\n const filePresent = fileExists();\n\n lines.push(`file: ${filePath} (exists=${filePresent})`);\n\n if (filePresent) {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(content) as Partial<Credentials>;\n const expired = parsed.expiresAt ? Date.now() >= parsed.expiresAt : 'unknown';\n lines.push(\n `file creds: userId=${parsed.userId ?? 'missing'}, expired=${expired}, hasRefreshToken=${!!parsed.refreshToken}`,\n );\n } catch (e) {\n lines.push(`file creds: parse error — ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n try {\n const entry = getKeyringEntry();\n const data = entry.getPassword();\n if (data) {\n const parsed = JSON.parse(data) as Partial<Credentials>;\n const expired = parsed.expiresAt ? Date.now() >= parsed.expiresAt : 'unknown';\n lines.push(\n `keyring: found, userId=${parsed.userId ?? 'missing'}, expired=${expired}, hasRefreshToken=${!!parsed.refreshToken}`,\n );\n } else {\n lines.push('keyring: empty (getPassword returned null)');\n }\n } catch (e) {\n lines.push(`keyring: error — ${e instanceof Error ? e.message : String(e)}`);\n }\n\n lines.push(`insecureStorage=${forceInsecureStorage}`);\n return lines;\n}\n\nexport { getCredentialsPath };\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type { StagingCache, Credentials } from './credential-store.js';
|
|
2
|
-
export { hasCredentials, getCredentials, saveCredentials, clearCredentials, updateTokens, getCredentialsPath, setInsecureStorage, } from './credential-store.js';
|
|
2
|
+
export { hasCredentials, getCredentials, saveCredentials, clearCredentials, updateTokens, getCredentialsPath, setInsecureStorage, diagnoseCredentials, } from './credential-store.js';
|
|
3
3
|
import type { Credentials } from './credential-store.js';
|
|
4
4
|
export declare function isTokenExpired(creds: Credentials): boolean;
|
|
5
5
|
export declare function getAccessToken(): string | null;
|
package/dist/lib/credentials.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { hasCredentials, getCredentials, saveCredentials, clearCredentials, updateTokens, getCredentialsPath, setInsecureStorage, } from './credential-store.js';
|
|
1
|
+
export { hasCredentials, getCredentials, saveCredentials, clearCredentials, updateTokens, getCredentialsPath, setInsecureStorage, diagnoseCredentials, } from './credential-store.js';
|
|
2
2
|
import { getCredentials, saveCredentials } from './credential-store.js';
|
|
3
3
|
export function isTokenExpired(creds) {
|
|
4
4
|
return Date.now() >= creds.expiresAt;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/lib/credentials.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/lib/credentials.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExE,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC,SAAS,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,cAAc,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,OAAO,KAAK,CAAC,WAAW,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,OAA6C;IAClF,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK;QAAE,OAAO;IAEnB,eAAe,CAAC;QACd,GAAG,KAAK;QACR,OAAO,EAAE;YACP,GAAG,OAAO;YACV,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE,OAAO;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,cAAc,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;AAC5E,CAAC","sourcesContent":["export type { StagingCache, Credentials } from './credential-store.js';\n\nexport {\n hasCredentials,\n getCredentials,\n saveCredentials,\n clearCredentials,\n updateTokens,\n getCredentialsPath,\n setInsecureStorage,\n diagnoseCredentials,\n} from './credential-store.js';\n\nimport type { Credentials } from './credential-store.js';\nimport { getCredentials, saveCredentials } from './credential-store.js';\n\nexport function isTokenExpired(creds: Credentials): boolean {\n return Date.now() >= creds.expiresAt;\n}\n\nexport function getAccessToken(): string | null {\n const creds = getCredentials();\n if (!creds) return null;\n if (isTokenExpired(creds)) return null;\n return creds.accessToken;\n}\n\nexport function saveStagingCredentials(staging: { clientId: string; apiKey: string }): void {\n const creds = getCredentials();\n if (!creds) return;\n\n saveCredentials({\n ...creds,\n staging: {\n ...staging,\n fetchedAt: Date.now(),\n },\n });\n}\n\nexport function getStagingCredentials(): { clientId: string; apiKey: string } | null {\n const creds = getCredentials();\n if (!creds?.staging) return null;\n if (isTokenExpired(creds)) return null;\n return { clientId: creds.staging.clientId, apiKey: creds.staging.apiKey };\n}\n"]}
|
|
@@ -9,6 +9,7 @@ import { DashboardAdapter } from './adapters/dashboard-adapter.js';
|
|
|
9
9
|
import { parseEnvFile } from '../utils/env-parser.js';
|
|
10
10
|
import { enableDebugLogs, initLogFile, logInfo, logError } from '../utils/debug.js';
|
|
11
11
|
import { getAccessToken, getCredentials, saveCredentials, getStagingCredentials, saveStagingCredentials, } from './credentials.js';
|
|
12
|
+
import { getConfig, saveConfig, getActiveEnvironment } from './config-store.js';
|
|
12
13
|
import { checkForEnvFiles, discoverCredentials } from './credential-discovery.js';
|
|
13
14
|
import { requestDeviceCode, pollForToken } from './device-auth.js';
|
|
14
15
|
import { fetchStagingCredentials as fetchStagingCredentialsApi } from './staging-api.js';
|
|
@@ -278,16 +279,36 @@ export async function runWithCore(options) {
|
|
|
278
279
|
return { result, deviceAuth };
|
|
279
280
|
}),
|
|
280
281
|
fetchStagingCredentials: fromPromise(async () => {
|
|
281
|
-
|
|
282
|
+
const activeEnv = getActiveEnvironment();
|
|
283
|
+
if (activeEnv?.clientId && activeEnv?.apiKey) {
|
|
284
|
+
return { clientId: activeEnv.clientId, apiKey: activeEnv.apiKey };
|
|
285
|
+
}
|
|
282
286
|
const cached = getStagingCredentials();
|
|
283
287
|
if (cached)
|
|
284
288
|
return cached;
|
|
285
|
-
// Fetch fresh from API
|
|
286
289
|
const token = getAccessToken();
|
|
287
290
|
if (!token)
|
|
288
291
|
throw new Error('No access token available');
|
|
289
292
|
const staging = await fetchStagingCredentialsApi(token);
|
|
290
293
|
saveStagingCredentials(staging);
|
|
294
|
+
try {
|
|
295
|
+
const config = getConfig() ?? { environments: {} };
|
|
296
|
+
if (!config.environments['default']) {
|
|
297
|
+
config.environments['default'] = {
|
|
298
|
+
name: 'default',
|
|
299
|
+
type: staging.apiKey.startsWith('sk_test_') ? 'sandbox' : 'production',
|
|
300
|
+
apiKey: staging.apiKey,
|
|
301
|
+
clientId: staging.clientId,
|
|
302
|
+
};
|
|
303
|
+
if (!config.activeEnvironment) {
|
|
304
|
+
config.activeEnvironment = 'default';
|
|
305
|
+
}
|
|
306
|
+
saveConfig(config);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// Don't block install if config-store write fails
|
|
311
|
+
}
|
|
291
312
|
return staging;
|
|
292
313
|
}),
|
|
293
314
|
// Branch check actors
|