opensteer 0.6.2 → 0.6.4
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/bin/opensteer.mjs +94 -8
- package/dist/{browser-profile-client-DK9qa_Dj.d.cts → browser-profile-client-D6PuRefA.d.cts} +1 -1
- package/dist/{browser-profile-client-CaL-mwqs.d.ts → browser-profile-client-OUHaODro.d.ts} +1 -1
- package/dist/{chunk-7RMY26CM.js → chunk-54KNQTOL.js} +172 -55
- package/dist/{chunk-WJI7TGBQ.js → chunk-6B6LOYU3.js} +1 -1
- package/dist/{chunk-F2VDVOJO.js → chunk-G6V2DJRN.js} +451 -609
- package/dist/chunk-K5CL76MG.js +81 -0
- package/dist/{chunk-WDRMHPWL.js → chunk-KPPOTU3D.js} +159 -180
- package/dist/cli/auth.cjs +186 -95
- package/dist/cli/auth.d.cts +1 -1
- package/dist/cli/auth.d.ts +1 -1
- package/dist/cli/auth.js +2 -2
- package/dist/cli/local-profile.cjs +197 -0
- package/dist/cli/local-profile.d.cts +18 -0
- package/dist/cli/local-profile.d.ts +18 -0
- package/dist/cli/local-profile.js +97 -0
- package/dist/cli/profile.cjs +1747 -1279
- package/dist/cli/profile.d.cts +2 -2
- package/dist/cli/profile.d.ts +2 -2
- package/dist/cli/profile.js +469 -7
- package/dist/cli/server.cjs +759 -257
- package/dist/cli/server.js +69 -16
- package/dist/index.cjs +688 -238
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +4 -3
- package/dist/{types-BxiRblC7.d.cts → types-BWItZPl_.d.cts} +31 -13
- package/dist/{types-BxiRblC7.d.ts → types-BWItZPl_.d.ts} +31 -13
- package/package.json +2 -2
- package/skills/opensteer/SKILL.md +34 -14
- package/skills/opensteer/references/cli-reference.md +1 -1
- package/skills/opensteer/references/examples.md +5 -3
- package/skills/opensteer/references/sdk-reference.md +16 -14
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/browser/chrome.ts
|
|
2
|
+
import { homedir, platform } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { existsSync, readFileSync } from "fs";
|
|
5
|
+
function detectChromePaths() {
|
|
6
|
+
const os = platform();
|
|
7
|
+
if (os === "darwin") {
|
|
8
|
+
const executable2 = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
|
9
|
+
return {
|
|
10
|
+
executable: existsSync(executable2) ? executable2 : null,
|
|
11
|
+
defaultUserDataDir: join(
|
|
12
|
+
homedir(),
|
|
13
|
+
"Library",
|
|
14
|
+
"Application Support",
|
|
15
|
+
"Google",
|
|
16
|
+
"Chrome"
|
|
17
|
+
)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (os === "win32") {
|
|
21
|
+
const executable2 = join(
|
|
22
|
+
process.env.PROGRAMFILES || "C:\\Program Files",
|
|
23
|
+
"Google",
|
|
24
|
+
"Chrome",
|
|
25
|
+
"Application",
|
|
26
|
+
"chrome.exe"
|
|
27
|
+
);
|
|
28
|
+
return {
|
|
29
|
+
executable: existsSync(executable2) ? executable2 : null,
|
|
30
|
+
defaultUserDataDir: join(
|
|
31
|
+
process.env.LOCALAPPDATA || join(homedir(), "AppData", "Local"),
|
|
32
|
+
"Google",
|
|
33
|
+
"Chrome",
|
|
34
|
+
"User Data"
|
|
35
|
+
)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const executable = "/usr/bin/google-chrome";
|
|
39
|
+
return {
|
|
40
|
+
executable: existsSync(executable) ? executable : null,
|
|
41
|
+
defaultUserDataDir: join(homedir(), ".config", "google-chrome")
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function expandHome(p) {
|
|
45
|
+
if (p.startsWith("~/") || p === "~") {
|
|
46
|
+
return join(homedir(), p.slice(1));
|
|
47
|
+
}
|
|
48
|
+
return p;
|
|
49
|
+
}
|
|
50
|
+
function listLocalChromeProfiles(userDataDir = detectChromePaths().defaultUserDataDir) {
|
|
51
|
+
const resolvedUserDataDir = expandHome(userDataDir);
|
|
52
|
+
const localStatePath = join(resolvedUserDataDir, "Local State");
|
|
53
|
+
if (!existsSync(localStatePath)) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const raw = JSON.parse(readFileSync(localStatePath, "utf-8"));
|
|
58
|
+
const infoCache = raw && typeof raw === "object" && !Array.isArray(raw) && raw.profile && typeof raw.profile === "object" && !Array.isArray(raw.profile) ? raw.profile.info_cache : void 0;
|
|
59
|
+
if (!infoCache || typeof infoCache !== "object") {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
return Object.entries(infoCache).map(([directory, info]) => {
|
|
63
|
+
const record = info && typeof info === "object" && !Array.isArray(info) ? info : {};
|
|
64
|
+
const name = typeof record.name === "string" && record.name.trim() ? record.name.trim() : directory;
|
|
65
|
+
return {
|
|
66
|
+
directory,
|
|
67
|
+
name
|
|
68
|
+
};
|
|
69
|
+
}).filter((profile) => profile.directory.trim().length > 0).sort(
|
|
70
|
+
(left, right) => left.directory.localeCompare(right.directory)
|
|
71
|
+
);
|
|
72
|
+
} catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
detectChromePaths,
|
|
79
|
+
expandHome,
|
|
80
|
+
listLocalChromeProfiles
|
|
81
|
+
};
|
|
@@ -261,14 +261,76 @@ import fs from "fs";
|
|
|
261
261
|
import path2 from "path";
|
|
262
262
|
import { fileURLToPath } from "url";
|
|
263
263
|
import { parse as parseDotenv } from "dotenv";
|
|
264
|
+
|
|
265
|
+
// src/cloud/credential-selection.ts
|
|
266
|
+
function selectCloudCredential(options) {
|
|
267
|
+
const apiKey = normalizeNonEmptyString(options.apiKey);
|
|
268
|
+
const accessToken = normalizeNonEmptyString(options.accessToken);
|
|
269
|
+
if (apiKey) {
|
|
270
|
+
if (options.authScheme === "bearer") {
|
|
271
|
+
return {
|
|
272
|
+
apiKey,
|
|
273
|
+
authScheme: "bearer",
|
|
274
|
+
kind: "access-token",
|
|
275
|
+
token: apiKey,
|
|
276
|
+
compatibilityBearerApiKey: true
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
apiKey,
|
|
281
|
+
authScheme: "api-key",
|
|
282
|
+
kind: "api-key",
|
|
283
|
+
token: apiKey
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
if (accessToken) {
|
|
287
|
+
return {
|
|
288
|
+
accessToken,
|
|
289
|
+
authScheme: "bearer",
|
|
290
|
+
kind: "access-token",
|
|
291
|
+
token: accessToken
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
function selectCloudCredentialByPrecedence(layers, authScheme) {
|
|
297
|
+
for (const layer of layers) {
|
|
298
|
+
const hasApiKey = layer.hasApiKey ?? Object.prototype.hasOwnProperty.call(layer, "apiKey");
|
|
299
|
+
const hasAccessToken = layer.hasAccessToken ?? Object.prototype.hasOwnProperty.call(layer, "accessToken");
|
|
300
|
+
if (!hasApiKey && !hasAccessToken) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
source: layer.source,
|
|
305
|
+
apiKey: layer.apiKey,
|
|
306
|
+
accessToken: layer.accessToken,
|
|
307
|
+
hasApiKey,
|
|
308
|
+
hasAccessToken,
|
|
309
|
+
credential: selectCloudCredential({
|
|
310
|
+
apiKey: layer.apiKey,
|
|
311
|
+
accessToken: layer.accessToken,
|
|
312
|
+
authScheme: layer.authScheme ?? authScheme
|
|
313
|
+
})
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
function normalizeNonEmptyString(value) {
|
|
319
|
+
if (typeof value !== "string") return void 0;
|
|
320
|
+
const normalized = value.trim();
|
|
321
|
+
return normalized.length ? normalized : void 0;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// src/config.ts
|
|
264
325
|
var DEFAULT_CONFIG = {
|
|
265
326
|
browser: {
|
|
266
327
|
headless: false,
|
|
267
328
|
executablePath: void 0,
|
|
268
329
|
slowMo: 0,
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
330
|
+
mode: void 0,
|
|
331
|
+
cdpUrl: void 0,
|
|
332
|
+
userDataDir: void 0,
|
|
333
|
+
profileDirectory: void 0
|
|
272
334
|
},
|
|
273
335
|
storage: {
|
|
274
336
|
rootDir: process.cwd()
|
|
@@ -552,11 +614,6 @@ function normalizeCloudOptions(value) {
|
|
|
552
614
|
}
|
|
553
615
|
return value;
|
|
554
616
|
}
|
|
555
|
-
function normalizeNonEmptyString(value) {
|
|
556
|
-
if (typeof value !== "string") return void 0;
|
|
557
|
-
const normalized = value.trim();
|
|
558
|
-
return normalized.length ? normalized : void 0;
|
|
559
|
-
}
|
|
560
617
|
function parseCloudEnabled(value, source) {
|
|
561
618
|
if (value == null) return void 0;
|
|
562
619
|
if (typeof value === "boolean") return value;
|
|
@@ -565,6 +622,18 @@ function parseCloudEnabled(value, source) {
|
|
|
565
622
|
`Invalid ${source} value "${String(value)}". Use true, false, or a cloud options object.`
|
|
566
623
|
);
|
|
567
624
|
}
|
|
625
|
+
function resolveCloudCredentialFields(selectedLayer) {
|
|
626
|
+
const credential = selectedLayer?.credential;
|
|
627
|
+
if (!credential) return {};
|
|
628
|
+
if (credential.kind === "api-key" || credential.compatibilityBearerApiKey === true && selectedLayer?.source !== "env") {
|
|
629
|
+
return {
|
|
630
|
+
apiKey: credential.token
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
return {
|
|
634
|
+
accessToken: credential.token
|
|
635
|
+
};
|
|
636
|
+
}
|
|
568
637
|
function resolveCloudSelection(config, env = process.env) {
|
|
569
638
|
const configCloud = parseCloudEnabled(config.cloud, "cloud");
|
|
570
639
|
if (configCloud !== void 0) {
|
|
@@ -601,7 +670,12 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
601
670
|
});
|
|
602
671
|
assertNoLegacyAiConfig(".opensteer/config.json", fileConfig);
|
|
603
672
|
assertNoLegacyRuntimeConfig(".opensteer/config.json", fileConfig);
|
|
673
|
+
const fileCloudOptions = normalizeCloudOptions(fileConfig.cloud);
|
|
674
|
+
const fileHasCloudApiKey = hasOwn(fileCloudOptions, "apiKey");
|
|
675
|
+
const fileHasCloudAccessToken = hasOwn(fileCloudOptions, "accessToken");
|
|
604
676
|
const fileRootDir = typeof fileConfig.storage?.rootDir === "string" ? fileConfig.storage.rootDir : void 0;
|
|
677
|
+
assertNoRemovedBrowserConfig(input.browser, "Opensteer constructor config");
|
|
678
|
+
assertNoRemovedBrowserConfig(fileConfig.browser, ".opensteer/config.json");
|
|
605
679
|
const envRootDir = input.storage?.rootDir ?? fileRootDir ?? initialRootDir;
|
|
606
680
|
const env = resolveEnv(envRootDir, {
|
|
607
681
|
debug: debugHint,
|
|
@@ -617,14 +691,30 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
617
691
|
"OPENSTEER_RUNTIME is no longer supported. Use OPENSTEER_MODE instead."
|
|
618
692
|
);
|
|
619
693
|
}
|
|
694
|
+
if (env.OPENSTEER_CONNECT_URL != null) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
"OPENSTEER_CONNECT_URL is no longer supported. Use OPENSTEER_CDP_URL instead."
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
if (env.OPENSTEER_CHANNEL != null) {
|
|
700
|
+
throw new Error(
|
|
701
|
+
"OPENSTEER_CHANNEL is no longer supported. Use OPENSTEER_BROWSER plus OPENSTEER_BROWSER_PATH when needed."
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
if (env.OPENSTEER_PROFILE_DIR != null) {
|
|
705
|
+
throw new Error(
|
|
706
|
+
"OPENSTEER_PROFILE_DIR is no longer supported. Use OPENSTEER_USER_DATA_DIR and OPENSTEER_PROFILE_DIRECTORY instead."
|
|
707
|
+
);
|
|
708
|
+
}
|
|
620
709
|
const envConfig = {
|
|
621
710
|
browser: {
|
|
622
711
|
headless: parseBool(env.OPENSTEER_HEADLESS),
|
|
623
712
|
executablePath: env.OPENSTEER_BROWSER_PATH || void 0,
|
|
624
713
|
slowMo: parseNumber(env.OPENSTEER_SLOW_MO),
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
714
|
+
mode: env.OPENSTEER_BROWSER === "real" || env.OPENSTEER_BROWSER === "chromium" ? env.OPENSTEER_BROWSER : void 0,
|
|
715
|
+
cdpUrl: env.OPENSTEER_CDP_URL || void 0,
|
|
716
|
+
userDataDir: env.OPENSTEER_USER_DATA_DIR || void 0,
|
|
717
|
+
profileDirectory: env.OPENSTEER_PROFILE_DIRECTORY || void 0
|
|
628
718
|
},
|
|
629
719
|
cursor: {
|
|
630
720
|
enabled: parseBool(env.OPENSTEER_CURSOR)
|
|
@@ -635,17 +725,38 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
635
725
|
const mergedWithFile = mergeDeep(runtimeDefaults, fileConfig);
|
|
636
726
|
const mergedWithEnv = mergeDeep(mergedWithFile, envConfig);
|
|
637
727
|
const resolved = mergeDeep(mergedWithEnv, input);
|
|
728
|
+
const browserHeadlessExplicit = input.browser?.headless !== void 0 || fileConfig.browser?.headless !== void 0 || envConfig.browser?.headless !== void 0;
|
|
729
|
+
if (!browserHeadlessExplicit && resolved.browser?.mode === "real") {
|
|
730
|
+
resolved.browser = {
|
|
731
|
+
...resolved.browser,
|
|
732
|
+
headless: true
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function assertNoRemovedBrowserConfig(value, source) {
|
|
736
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
const record = value;
|
|
740
|
+
if (record.connectUrl !== void 0) {
|
|
741
|
+
throw new Error(
|
|
742
|
+
`${source}.browser.connectUrl is no longer supported. Use browser.cdpUrl instead.`
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
if (record.channel !== void 0) {
|
|
746
|
+
throw new Error(
|
|
747
|
+
`${source}.browser.channel is no longer supported. Use browser.mode plus browser.executablePath instead.`
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
if (record.profileDir !== void 0) {
|
|
751
|
+
throw new Error(
|
|
752
|
+
`${source}.browser.profileDir is no longer supported. Use browser.userDataDir and browser.profileDirectory instead.`
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
638
756
|
const envApiKey = resolveOpensteerApiKey(env);
|
|
639
757
|
const envAccessTokenRaw = resolveOpensteerAccessToken(env);
|
|
640
758
|
const envBaseUrl = resolveOpensteerBaseUrl(env);
|
|
641
759
|
const envAuthScheme = resolveOpensteerAuthScheme(env);
|
|
642
|
-
if (envApiKey && envAccessTokenRaw) {
|
|
643
|
-
throw new Error(
|
|
644
|
-
"OPENSTEER_API_KEY and OPENSTEER_ACCESS_TOKEN are mutually exclusive. Set only one."
|
|
645
|
-
);
|
|
646
|
-
}
|
|
647
|
-
const envAccessToken = envAccessTokenRaw || (envAuthScheme === "bearer" ? envApiKey : void 0);
|
|
648
|
-
const envApiCredential = envAuthScheme === "bearer" && !envAccessTokenRaw ? void 0 : envApiKey;
|
|
649
760
|
const envCloudProfileId = resolveOpensteerCloudProfileId(env);
|
|
650
761
|
const envCloudProfileReuseIfActive = resolveOpensteerCloudProfileReuseIfActive(env);
|
|
651
762
|
const envCloudAnnounce = parseCloudAnnounce(
|
|
@@ -674,11 +785,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
674
785
|
const inputHasCloudBaseUrl = Boolean(
|
|
675
786
|
inputCloudOptions && Object.prototype.hasOwnProperty.call(inputCloudOptions, "baseUrl")
|
|
676
787
|
);
|
|
677
|
-
if (normalizeNonEmptyString(inputCloudOptions?.apiKey) && normalizeNonEmptyString(inputCloudOptions?.accessToken)) {
|
|
678
|
-
throw new Error(
|
|
679
|
-
"cloud.apiKey and cloud.accessToken are mutually exclusive. Set only one."
|
|
680
|
-
);
|
|
681
|
-
}
|
|
682
788
|
const cloudSelection = resolveCloudSelection({
|
|
683
789
|
cloud: resolved.cloud
|
|
684
790
|
}, env);
|
|
@@ -689,11 +795,6 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
689
795
|
accessToken: resolvedCloudAccessTokenRaw,
|
|
690
796
|
...resolvedCloudRest
|
|
691
797
|
} = resolvedCloud;
|
|
692
|
-
if (normalizeNonEmptyString(resolvedCloudApiKeyRaw) && normalizeNonEmptyString(resolvedCloudAccessTokenRaw)) {
|
|
693
|
-
throw new Error(
|
|
694
|
-
"Cloud config cannot include both apiKey and accessToken at the same time."
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
798
|
const resolvedCloudBrowserProfile = normalizeCloudBrowserProfileOptions(
|
|
698
799
|
resolvedCloud.browserProfile,
|
|
699
800
|
"resolved.cloud.browserProfile"
|
|
@@ -705,25 +806,40 @@ function resolveConfigWithEnv(input = {}, options = {}) {
|
|
|
705
806
|
const browserProfile = inputCloudBrowserProfile ?? envCloudBrowserProfile ?? resolvedCloudBrowserProfile;
|
|
706
807
|
let authScheme = inputAuthScheme ?? envAuthScheme ?? parseAuthScheme(resolvedCloud.authScheme, "cloud.authScheme") ?? "api-key";
|
|
707
808
|
const announce = inputCloudAnnounce ?? envCloudAnnounce ?? parseCloudAnnounce(resolvedCloud.announce, "cloud.announce") ?? "always";
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
809
|
+
const selectedCredentialLayer = selectCloudCredentialByPrecedence(
|
|
810
|
+
[
|
|
811
|
+
{
|
|
812
|
+
source: "input",
|
|
813
|
+
apiKey: inputCloudOptions?.apiKey,
|
|
814
|
+
accessToken: inputCloudOptions?.accessToken,
|
|
815
|
+
hasApiKey: inputHasCloudApiKey,
|
|
816
|
+
hasAccessToken: inputHasCloudAccessToken
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
source: "env",
|
|
820
|
+
apiKey: envApiKey,
|
|
821
|
+
accessToken: envAccessTokenRaw,
|
|
822
|
+
hasApiKey: envApiKey !== void 0,
|
|
823
|
+
hasAccessToken: envAccessTokenRaw !== void 0
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
source: "file",
|
|
827
|
+
apiKey: fileCloudOptions?.apiKey,
|
|
828
|
+
accessToken: fileCloudOptions?.accessToken,
|
|
829
|
+
hasApiKey: fileHasCloudApiKey,
|
|
830
|
+
hasAccessToken: fileHasCloudAccessToken
|
|
831
|
+
}
|
|
832
|
+
],
|
|
833
|
+
authScheme
|
|
834
|
+
);
|
|
835
|
+
const { apiKey, accessToken } = resolveCloudCredentialFields(selectedCredentialLayer);
|
|
720
836
|
if (accessToken) {
|
|
721
837
|
authScheme = "bearer";
|
|
722
838
|
}
|
|
723
839
|
resolved.cloud = {
|
|
724
840
|
...resolvedCloudRest,
|
|
725
|
-
...
|
|
726
|
-
...
|
|
841
|
+
...apiKey ? { apiKey } : selectedCredentialLayer?.hasApiKey && !accessToken ? { apiKey: selectedCredentialLayer.apiKey } : {},
|
|
842
|
+
...accessToken ? { accessToken } : selectedCredentialLayer?.hasAccessToken && !apiKey ? { accessToken: selectedCredentialLayer.accessToken } : {},
|
|
727
843
|
authScheme,
|
|
728
844
|
announce,
|
|
729
845
|
...browserProfile ? { browserProfile } : {}
|
|
@@ -1159,147 +1275,10 @@ function readCloudActionDescription(payload) {
|
|
|
1159
1275
|
return normalized.length ? normalized : void 0;
|
|
1160
1276
|
}
|
|
1161
1277
|
|
|
1162
|
-
// src/auth/keychain-store.ts
|
|
1163
|
-
import { spawnSync } from "child_process";
|
|
1164
|
-
function commandExists(command) {
|
|
1165
|
-
const result = spawnSync(command, ["--help"], {
|
|
1166
|
-
encoding: "utf8",
|
|
1167
|
-
stdio: "ignore"
|
|
1168
|
-
});
|
|
1169
|
-
return result.error == null;
|
|
1170
|
-
}
|
|
1171
|
-
function commandFailed(result) {
|
|
1172
|
-
return typeof result.status === "number" && result.status !== 0;
|
|
1173
|
-
}
|
|
1174
|
-
function sanitizeCommandArgs(command, args) {
|
|
1175
|
-
if (command !== "security") {
|
|
1176
|
-
return args;
|
|
1177
|
-
}
|
|
1178
|
-
const sanitized = [];
|
|
1179
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
1180
|
-
const value = args[index];
|
|
1181
|
-
sanitized.push(value);
|
|
1182
|
-
if (value === "-w" && index + 1 < args.length) {
|
|
1183
|
-
sanitized.push("[REDACTED]");
|
|
1184
|
-
index += 1;
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
return sanitized;
|
|
1188
|
-
}
|
|
1189
|
-
function buildCommandError(command, args, result) {
|
|
1190
|
-
const stderr = typeof result.stderr === "string" && result.stderr.trim() ? result.stderr.trim() : `Command "${command}" failed with status ${String(result.status)}.`;
|
|
1191
|
-
const sanitizedArgs = sanitizeCommandArgs(command, args);
|
|
1192
|
-
return new Error(
|
|
1193
|
-
[
|
|
1194
|
-
`Unable to persist credential via ${command}.`,
|
|
1195
|
-
`${command} ${sanitizedArgs.join(" ")}`,
|
|
1196
|
-
stderr
|
|
1197
|
-
].join(" ")
|
|
1198
|
-
);
|
|
1199
|
-
}
|
|
1200
|
-
function createMacosSecurityStore() {
|
|
1201
|
-
return {
|
|
1202
|
-
backend: "macos-security",
|
|
1203
|
-
get(service, account) {
|
|
1204
|
-
const result = spawnSync(
|
|
1205
|
-
"security",
|
|
1206
|
-
["find-generic-password", "-s", service, "-a", account, "-w"],
|
|
1207
|
-
{ encoding: "utf8" }
|
|
1208
|
-
);
|
|
1209
|
-
if (commandFailed(result)) {
|
|
1210
|
-
return null;
|
|
1211
|
-
}
|
|
1212
|
-
const secret = result.stdout.trim();
|
|
1213
|
-
return secret.length ? secret : null;
|
|
1214
|
-
},
|
|
1215
|
-
set(service, account, secret) {
|
|
1216
|
-
const args = [
|
|
1217
|
-
"add-generic-password",
|
|
1218
|
-
"-U",
|
|
1219
|
-
"-s",
|
|
1220
|
-
service,
|
|
1221
|
-
"-a",
|
|
1222
|
-
account,
|
|
1223
|
-
"-w",
|
|
1224
|
-
secret
|
|
1225
|
-
];
|
|
1226
|
-
const result = spawnSync("security", args, { encoding: "utf8" });
|
|
1227
|
-
if (commandFailed(result)) {
|
|
1228
|
-
throw buildCommandError("security", args, result);
|
|
1229
|
-
}
|
|
1230
|
-
},
|
|
1231
|
-
delete(service, account) {
|
|
1232
|
-
const args = ["delete-generic-password", "-s", service, "-a", account];
|
|
1233
|
-
const result = spawnSync("security", args, { encoding: "utf8" });
|
|
1234
|
-
if (commandFailed(result)) {
|
|
1235
|
-
return;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
};
|
|
1239
|
-
}
|
|
1240
|
-
function createLinuxSecretToolStore() {
|
|
1241
|
-
return {
|
|
1242
|
-
backend: "linux-secret-tool",
|
|
1243
|
-
get(service, account) {
|
|
1244
|
-
const result = spawnSync(
|
|
1245
|
-
"secret-tool",
|
|
1246
|
-
["lookup", "service", service, "account", account],
|
|
1247
|
-
{
|
|
1248
|
-
encoding: "utf8"
|
|
1249
|
-
}
|
|
1250
|
-
);
|
|
1251
|
-
if (commandFailed(result)) {
|
|
1252
|
-
return null;
|
|
1253
|
-
}
|
|
1254
|
-
const secret = result.stdout.trim();
|
|
1255
|
-
return secret.length ? secret : null;
|
|
1256
|
-
},
|
|
1257
|
-
set(service, account, secret) {
|
|
1258
|
-
const args = [
|
|
1259
|
-
"store",
|
|
1260
|
-
"--label",
|
|
1261
|
-
"Opensteer CLI",
|
|
1262
|
-
"service",
|
|
1263
|
-
service,
|
|
1264
|
-
"account",
|
|
1265
|
-
account
|
|
1266
|
-
];
|
|
1267
|
-
const result = spawnSync("secret-tool", args, {
|
|
1268
|
-
encoding: "utf8",
|
|
1269
|
-
input: secret
|
|
1270
|
-
});
|
|
1271
|
-
if (commandFailed(result)) {
|
|
1272
|
-
throw buildCommandError("secret-tool", args, result);
|
|
1273
|
-
}
|
|
1274
|
-
},
|
|
1275
|
-
delete(service, account) {
|
|
1276
|
-
const args = ["clear", "service", service, "account", account];
|
|
1277
|
-
spawnSync("secret-tool", args, {
|
|
1278
|
-
encoding: "utf8"
|
|
1279
|
-
});
|
|
1280
|
-
}
|
|
1281
|
-
};
|
|
1282
|
-
}
|
|
1283
|
-
function createKeychainStore() {
|
|
1284
|
-
if (process.platform === "darwin") {
|
|
1285
|
-
if (!commandExists("security")) {
|
|
1286
|
-
return null;
|
|
1287
|
-
}
|
|
1288
|
-
return createMacosSecurityStore();
|
|
1289
|
-
}
|
|
1290
|
-
if (process.platform === "linux") {
|
|
1291
|
-
if (!commandExists("secret-tool")) {
|
|
1292
|
-
return null;
|
|
1293
|
-
}
|
|
1294
|
-
return createLinuxSecretToolStore();
|
|
1295
|
-
}
|
|
1296
|
-
return null;
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
1278
|
export {
|
|
1300
|
-
createKeychainStore,
|
|
1301
1279
|
extractErrorMessage,
|
|
1302
1280
|
normalizeError,
|
|
1281
|
+
selectCloudCredential,
|
|
1303
1282
|
normalizeNamespace,
|
|
1304
1283
|
resolveNamespaceDir,
|
|
1305
1284
|
resolveCloudSelection,
|