snow-flow 10.0.12 → 10.0.13
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/package.json
CHANGED
|
@@ -133,27 +133,37 @@ This file contains instructions for AI agents working in this codebase.
|
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
135
|
* Try to open a URL in the user's browser.
|
|
136
|
-
*
|
|
136
|
+
* Tries multiple strategies:
|
|
137
|
+
* 1. $BROWSER env var (set by Codespaces / Gitpod / custom setups)
|
|
138
|
+
* 2. Platform-native: open (macOS), cmd /c start (Windows), xdg-open (Linux)
|
|
137
139
|
*/
|
|
138
140
|
async function tryOpenBrowser(url: string): Promise<boolean> {
|
|
139
141
|
const { spawn } = await import("child_process")
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
proc = spawn(
|
|
145
|
-
|
|
146
|
-
proc
|
|
147
|
-
|
|
148
|
-
|
|
142
|
+
|
|
143
|
+
const trySpawn = (cmd: string, args: string[]): Promise<boolean> =>
|
|
144
|
+
new Promise((resolve) => {
|
|
145
|
+
try {
|
|
146
|
+
const proc = spawn(cmd, args, { detached: true, stdio: "ignore" })
|
|
147
|
+
if (proc.unref) proc.unref()
|
|
148
|
+
proc.on("error", () => resolve(false))
|
|
149
|
+
// Give the process a moment to fail, then assume success
|
|
150
|
+
setTimeout(() => resolve(true), 200)
|
|
151
|
+
} catch {
|
|
152
|
+
resolve(false)
|
|
149
153
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// 1. Try $BROWSER env var (Codespaces, Gitpod, etc.)
|
|
157
|
+
const browserEnv = process.env.BROWSER
|
|
158
|
+
if (browserEnv) {
|
|
159
|
+
const opened = await trySpawn(browserEnv, [url])
|
|
160
|
+
if (opened) return true
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 2. Platform-native
|
|
164
|
+
if (process.platform === "darwin") return trySpawn("open", [url])
|
|
165
|
+
if (process.platform === "win32") return trySpawn("cmd", ["/c", "start", url])
|
|
166
|
+
return trySpawn("xdg-open", [url])
|
|
157
167
|
}
|
|
158
168
|
|
|
159
169
|
type AuthMethod = "servicenow-oauth" | "servicenow-basic" | "enterprise-portal" | "enterprise-license" | "enterprise-combined" | "servicenow-llm"
|
|
@@ -368,6 +378,24 @@ function DialogAuthServiceNowOAuth() {
|
|
|
368
378
|
setTimeout(() => secretInput?.focus(), 10)
|
|
369
379
|
}
|
|
370
380
|
}
|
|
381
|
+
// Keyboard shortcuts for headless auth step
|
|
382
|
+
if (step() === "callback-paste" && headlessAuthUrl()) {
|
|
383
|
+
if (evt.name === "o") {
|
|
384
|
+
tryOpenBrowser(headlessAuthUrl()).then((opened) => {
|
|
385
|
+
toast.show({
|
|
386
|
+
variant: opened ? "success" : "error",
|
|
387
|
+
message: opened ? "Browser opened!" : "Could not open browser",
|
|
388
|
+
duration: 3000,
|
|
389
|
+
})
|
|
390
|
+
})
|
|
391
|
+
}
|
|
392
|
+
if (evt.name === "c") {
|
|
393
|
+
Clipboard.copy(headlessAuthUrl()).then(
|
|
394
|
+
() => toast.show({ variant: "success", message: "URL copied to clipboard!", duration: 3000 }),
|
|
395
|
+
() => toast.show({ variant: "error", message: "Failed to copy", duration: 3000 }),
|
|
396
|
+
)
|
|
397
|
+
}
|
|
398
|
+
}
|
|
371
399
|
})
|
|
372
400
|
|
|
373
401
|
const startMcpServerAfterAuth = async () => {
|
|
@@ -638,14 +666,30 @@ function DialogAuthServiceNowOAuth() {
|
|
|
638
666
|
paddingLeft={2}
|
|
639
667
|
paddingRight={2}
|
|
640
668
|
backgroundColor={theme.primary}
|
|
669
|
+
onMouseUp={() => {
|
|
670
|
+
tryOpenBrowser(headlessAuthUrl()).then((opened) => {
|
|
671
|
+
toast.show({
|
|
672
|
+
variant: opened ? "success" : "error",
|
|
673
|
+
message: opened ? "Browser opened!" : "Could not open browser",
|
|
674
|
+
duration: 3000,
|
|
675
|
+
})
|
|
676
|
+
})
|
|
677
|
+
}}
|
|
678
|
+
>
|
|
679
|
+
<text fg={theme.selectedListItemText} attributes={TextAttributes.BOLD}>[ Open in Browser ]</text>
|
|
680
|
+
</box>
|
|
681
|
+
<box
|
|
682
|
+
paddingLeft={2}
|
|
683
|
+
paddingRight={2}
|
|
684
|
+
backgroundColor={theme.backgroundElement}
|
|
641
685
|
onMouseUp={() => {
|
|
642
686
|
Clipboard.copy(headlessAuthUrl()).then(
|
|
643
|
-
() => toast.show({ variant: "success", message: "URL copied
|
|
644
|
-
() => toast.show({ variant: "error", message: "Failed to copy
|
|
687
|
+
() => toast.show({ variant: "success", message: "URL copied!", duration: 3000 }),
|
|
688
|
+
() => toast.show({ variant: "error", message: "Failed to copy", duration: 3000 }),
|
|
645
689
|
)
|
|
646
690
|
}}
|
|
647
691
|
>
|
|
648
|
-
<text fg={theme.
|
|
692
|
+
<text fg={theme.text} attributes={TextAttributes.BOLD}>[ Copy URL ]</text>
|
|
649
693
|
</box>
|
|
650
694
|
</box>
|
|
651
695
|
<box paddingTop={1}>
|
|
@@ -654,6 +698,14 @@ function DialogAuthServiceNowOAuth() {
|
|
|
654
698
|
<text fg={theme.textMuted}> 2. The page may show an error (this is expected)</text>
|
|
655
699
|
<text fg={theme.textMuted}> 3. Copy the FULL URL from your browser address bar</text>
|
|
656
700
|
</box>
|
|
701
|
+
<box flexDirection="row" gap={1}>
|
|
702
|
+
<text fg={theme.text}>o</text>
|
|
703
|
+
<text fg={theme.textMuted}>open</text>
|
|
704
|
+
<text fg={theme.text}> c</text>
|
|
705
|
+
<text fg={theme.textMuted}>copy</text>
|
|
706
|
+
<text fg={theme.text}> esc</text>
|
|
707
|
+
<text fg={theme.textMuted}>back</text>
|
|
708
|
+
</box>
|
|
657
709
|
</Show>
|
|
658
710
|
<text fg={theme.text}>Paste the callback URL from your browser address bar:</text>
|
|
659
711
|
<textarea
|