Inline base64 source maps had been checked into tracked src files. This strips those comments from the repository without changing runtime behavior or adding ongoing guardrails, per the requested one-time cleanup scope. Constraint: Keep this change limited to tracked source cleanup only Rejected: Add CI/source verification guard | user requested one-time cleanup only Confidence: high Scope-risk: narrow Reversibility: clean Directive: If these directives reappear, fix the producing transform instead of reintroducing repo-side cleanup code Tested: rg -n "sourceMappingURL" ., bun run smoke, bun run verify:privacy, bun run test:provider, npm run test:provider-recommendation Not-tested: bun run typecheck (repository has many pre-existing unrelated failures) Co-authored-by: anandh8x <test@example.com>
187 lines
5.9 KiB
TypeScript
187 lines
5.9 KiB
TypeScript
import { execa } from 'execa';
|
|
import * as React from 'react';
|
|
import { useEffect, useState } from 'react';
|
|
import { Select } from '../../components/CustomSelect/index.js';
|
|
import { Dialog } from '../../components/design-system/Dialog.js';
|
|
import { LoadingState } from '../../components/design-system/LoadingState.js';
|
|
import { Box, Text } from '../../ink.js';
|
|
import { logEvent, type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS as SafeString } from '../../services/analytics/index.js';
|
|
import type { LocalJSXCommandOnDone } from '../../types/command.js';
|
|
import { openBrowser } from '../../utils/browser.js';
|
|
import { getGhAuthStatus } from '../../utils/github/ghAuthStatus.js';
|
|
import { createDefaultEnvironment, getCodeWebUrl, type ImportTokenError, importGithubToken, isSignedIn, RedactedGithubToken } from './api.js';
|
|
type CheckResult = {
|
|
status: 'not_signed_in';
|
|
} | {
|
|
status: 'has_gh_token';
|
|
token: RedactedGithubToken;
|
|
} | {
|
|
status: 'gh_not_installed';
|
|
} | {
|
|
status: 'gh_not_authenticated';
|
|
};
|
|
async function checkLoginState(): Promise<CheckResult> {
|
|
if (!(await isSignedIn())) {
|
|
return {
|
|
status: 'not_signed_in'
|
|
};
|
|
}
|
|
const ghStatus = await getGhAuthStatus();
|
|
if (ghStatus === 'not_installed') {
|
|
return {
|
|
status: 'gh_not_installed'
|
|
};
|
|
}
|
|
if (ghStatus === 'not_authenticated') {
|
|
return {
|
|
status: 'gh_not_authenticated'
|
|
};
|
|
}
|
|
|
|
// ghStatus === 'authenticated'. getGhAuthStatus spawns with stdout:'ignore'
|
|
// (telemetry-safe); spawn once more with stdout:'pipe' to read the token.
|
|
const {
|
|
stdout
|
|
} = await execa('gh', ['auth', 'token'], {
|
|
stdout: 'pipe',
|
|
stderr: 'ignore',
|
|
timeout: 5000,
|
|
reject: false
|
|
});
|
|
const trimmed = stdout.trim();
|
|
if (!trimmed) {
|
|
return {
|
|
status: 'gh_not_authenticated'
|
|
};
|
|
}
|
|
return {
|
|
status: 'has_gh_token',
|
|
token: new RedactedGithubToken(trimmed)
|
|
};
|
|
}
|
|
function errorMessage(err: ImportTokenError, codeUrl: string): string {
|
|
switch (err.kind) {
|
|
case 'not_signed_in':
|
|
return `Login failed. Please visit ${codeUrl} and login using the GitHub App`;
|
|
case 'invalid_token':
|
|
return 'GitHub rejected that token. Run `gh auth login` and try again.';
|
|
case 'server':
|
|
return `Server error (${err.status}). Try again in a moment.`;
|
|
case 'network':
|
|
return "Couldn't reach the server. Check your connection.";
|
|
}
|
|
}
|
|
type Step = {
|
|
name: 'checking';
|
|
} | {
|
|
name: 'confirm';
|
|
token: RedactedGithubToken;
|
|
} | {
|
|
name: 'uploading';
|
|
};
|
|
function Web({
|
|
onDone
|
|
}: {
|
|
onDone: LocalJSXCommandOnDone;
|
|
}) {
|
|
const [step, setStep] = useState<Step>({
|
|
name: 'checking'
|
|
});
|
|
useEffect(() => {
|
|
logEvent('tengu_remote_setup_started', {});
|
|
void checkLoginState().then(async result => {
|
|
switch (result.status) {
|
|
case 'not_signed_in':
|
|
logEvent('tengu_remote_setup_result', {
|
|
result: 'not_signed_in' as SafeString
|
|
});
|
|
onDone('Not signed in to Claude. Run /login first.');
|
|
return;
|
|
case 'gh_not_installed':
|
|
case 'gh_not_authenticated':
|
|
{
|
|
const url = `${getCodeWebUrl()}/onboarding?step=alt-auth`;
|
|
await openBrowser(url);
|
|
logEvent('tengu_remote_setup_result', {
|
|
result: result.status as SafeString
|
|
});
|
|
onDone(result.status === 'gh_not_installed' ? `GitHub CLI not found. Install it via https://cli.github.com/, then run \`gh auth login\`, or connect GitHub on the web: ${url}` : `GitHub CLI not authenticated. Run \`gh auth login\` and try again, or connect GitHub on the web: ${url}`);
|
|
return;
|
|
}
|
|
case 'has_gh_token':
|
|
setStep({
|
|
name: 'confirm',
|
|
token: result.token
|
|
});
|
|
}
|
|
});
|
|
// onDone is stable across renders; intentionally not in deps.
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
const handleCancel = () => {
|
|
logEvent('tengu_remote_setup_result', {
|
|
result: 'cancelled' as SafeString
|
|
});
|
|
onDone();
|
|
};
|
|
const handleConfirm = async (token: RedactedGithubToken) => {
|
|
setStep({
|
|
name: 'uploading'
|
|
});
|
|
const result = await importGithubToken(token);
|
|
if (!result.ok) {
|
|
logEvent('tengu_remote_setup_result', {
|
|
result: 'import_failed' as SafeString,
|
|
error_kind: result.error.kind as SafeString
|
|
});
|
|
onDone(errorMessage(result.error, getCodeWebUrl()));
|
|
return;
|
|
}
|
|
|
|
// Token import succeeded. Environment creation is best-effort — if it
|
|
// fails, the web state machine routes to env-setup on landing, which is
|
|
// one extra click but still better than the OAuth dance.
|
|
await createDefaultEnvironment();
|
|
const url = getCodeWebUrl();
|
|
await openBrowser(url);
|
|
logEvent('tengu_remote_setup_result', {
|
|
result: 'success' as SafeString
|
|
});
|
|
onDone(`Connected as ${result.result.github_username}. Opened ${url}`);
|
|
};
|
|
if (step.name === 'checking') {
|
|
return <LoadingState message="Checking login status…" />;
|
|
}
|
|
if (step.name === 'uploading') {
|
|
return <LoadingState message="Connecting GitHub to Claude…" />;
|
|
}
|
|
const token = step.token;
|
|
return <Dialog title="Connect Claude on the web to GitHub?" onCancel={handleCancel} hideInputGuide>
|
|
<Box flexDirection="column">
|
|
<Text>
|
|
Claude on the web requires connecting to your GitHub account to clone
|
|
and push code on your behalf.
|
|
</Text>
|
|
<Text dimColor>
|
|
Your local credentials are used to authenticate with GitHub
|
|
</Text>
|
|
</Box>
|
|
<Select options={[{
|
|
label: 'Continue',
|
|
value: 'send'
|
|
}, {
|
|
label: 'Cancel',
|
|
value: 'cancel'
|
|
}]} onChange={value => {
|
|
if (value === 'send') {
|
|
void handleConfirm(token);
|
|
} else {
|
|
handleCancel();
|
|
}
|
|
}} onCancel={handleCancel} />
|
|
</Dialog>;
|
|
}
|
|
export async function call(onDone: LocalJSXCommandOnDone): Promise<React.ReactNode> {
|
|
return <Web onDone={onDone} />;
|
|
}
|