@@ -22,7 +22,8 @@ export const grokImageName = "docker-git-auth-grok:latest"
2222export const grokImageDir = ".docker-git/.orch/auth/grok/.image"
2323export const grokContainerHomeDir = "/grok-home"
2424export const grokCredentialsDir = ".grok"
25- export const grokDevPackageSpec = "grok-dev@1.1.7"
25+ export const grokCliInstallScriptUrl = "https://x.ai/cli/install.sh"
26+ export const grokCliVersion = "0.1.211"
2627
2728export type GrokAccountContext = {
2829 readonly accountLabel : string
@@ -41,27 +42,34 @@ export const grokEnvFilePath = (accountPath: string): string => `${accountPath}/
4142export const grokCredentialsPath = ( accountPath : string ) : string => `${ accountPath } /${ grokCredentialsDir } `
4243export const grokUserSettingsPath = ( accountPath : string ) : string =>
4344 `${ grokCredentialsPath ( accountPath ) } /user-settings.json`
45+ export const grokAuthJsonPath = ( accountPath : string ) : string => `${ grokCredentialsPath ( accountPath ) } /auth.json`
46+
47+ const grokEnvApiKeyNames : ReadonlyArray < string > = [ "GROK_DEPLOYMENT_KEY" , "GROK_API_KEY" , "XAI_API_KEY" ]
4448
4549// CHANGE: render Dockerfile for Grok CLI authentication image
4650// WHY: Grok browser/OAuth auth must run in an isolated shell that persists ~/.grok
4751// QUOTE(ТЗ): "Signing in with Grok..."
4852// REF: issue-304
49- // SOURCE: https://www.npmjs.com/package /grok-dev
53+ // SOURCE: https://x.ai/news /grok-build-cli
5054// FORMAT THEOREM: renderGrokDockerfile() -> valid_dockerfile
5155// PURITY: CORE
5256// EFFECT: n/a
53- // INVARIANT: image includes Node.js and a grok executable
57+ // INVARIANT: image includes the official xAI grok executable
5458// COMPLEXITY: O(1)
5559export const renderGrokDockerfile = ( ) : string =>
5660 String . raw `FROM ubuntu:24.04
5761ENV DEBIAN_FRONTEND=noninteractive
5862RUN apt-get update \
5963 && apt-get install -y --no-install-recommends ca-certificates curl bsdutils \
6064 && rm -rf /var/lib/apt/lists/*
61- RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
62- && apt-get install -y --no-install-recommends nodejs \
63- && rm -rf /var/lib/apt/lists/*
64- RUN npm install -g ${ grokDevPackageSpec }
65+ RUN set -eu; \
66+ curl -fsSL --retry 5 --retry-all-errors --retry-delay 2 ${ grokCliInstallScriptUrl } -o /tmp/grok-install.sh; \
67+ HOME=/tmp/grok-install-home GROK_BIN_DIR=/usr/local/bin bash /tmp/grok-install.sh ${ grokCliVersion } ; \
68+ install -m 0755 "$(readlink -f /usr/local/bin/grok)" /usr/local/bin/grok.real; \
69+ install -m 0755 "$(readlink -f /usr/local/bin/agent)" /usr/local/bin/agent.real; \
70+ mv -f /usr/local/bin/grok.real /usr/local/bin/grok; \
71+ mv -f /usr/local/bin/agent.real /usr/local/bin/agent; \
72+ rm -rf /tmp/grok-install.sh /tmp/grok-install-home
6573RUN grok --version
6674`
6775
@@ -127,12 +135,15 @@ const readApiKeyFromEnvFile = (
127135 const envContent = yield * _ ( fs . readFileString ( envFilePath ) , Effect . orElseSucceed ( ( ) => "" ) )
128136 for ( const line of envContent . split ( "\n" ) ) {
129137 const trimmed = line . trim ( )
130- if ( ! trimmed . startsWith ( "GROK_API_KEY=" ) ) {
131- continue
132- }
133- const value = trimmed . slice ( "GROK_API_KEY=" . length ) . replaceAll ( / ^ [ ' " ] | [ ' " ] $ / g, "" ) . trim ( )
134- if ( value . length > 0 ) {
135- return value
138+ for ( const key of grokEnvApiKeyNames ) {
139+ const prefix = `${ key } =`
140+ if ( ! trimmed . startsWith ( prefix ) ) {
141+ continue
142+ }
143+ const value = trimmed . slice ( prefix . length ) . replaceAll ( / ^ [ ' " ] | [ ' " ] $ / g, "" ) . trim ( )
144+ if ( value . length > 0 ) {
145+ return value
146+ }
136147 }
137148 }
138149 return null
@@ -165,6 +176,13 @@ export const hasGrokCredentials = (
165176 if ( apiKey !== null ) {
166177 return true
167178 }
179+ const hasAuthJson = yield * _ ( isRegularFile ( fs , grokAuthJsonPath ( accountPath ) ) )
180+ if ( hasAuthJson ) {
181+ const authJson = yield * _ ( fs . readFileString ( grokAuthJsonPath ( accountPath ) ) , Effect . orElseSucceed ( ( ) => "" ) )
182+ if ( hasGrokAuthJsonCredentials ( authJson ) ) {
183+ return true
184+ }
185+ }
168186 const hasUserSettings = yield * _ ( isRegularFile ( fs , grokUserSettingsPath ( accountPath ) ) )
169187 if ( ! hasUserSettings ) {
170188 return false
@@ -184,6 +202,15 @@ const grokUserSettingsCredentialMarkers: ReadonlyArray<RegExp> = [
184202const hasGrokUserSettingsCredentials = ( content : string ) : boolean =>
185203 grokUserSettingsCredentialMarkers . some ( ( marker ) => marker . test ( content ) )
186204
205+ const grokAuthJsonCredentialMarkers : ReadonlyArray < RegExp > = [
206+ / " k e y " \s * : \s * " [ ^ " ] + " / u,
207+ / " t o k e n " \s * : \s * " [ ^ " ] + " / u,
208+ / " a c c e s s T o k e n " \s * : \s * " [ ^ " ] + " / u
209+ ]
210+
211+ const hasGrokAuthJsonCredentials = ( content : string ) : boolean =>
212+ grokAuthJsonCredentialMarkers . some ( ( marker ) => marker . test ( content ) )
213+
187214export const resolveGrokAuthMethod = (
188215 fs : FileSystem . FileSystem ,
189216 accountPath : string
0 commit comments