@raindrop-ai/wizard 0.0.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/LICENSE +47 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +117 -0
- package/dist/bin.js.map +1 -0
- package/dist/src/docs/browser.md +105 -0
- package/dist/src/docs/python.md +618 -0
- package/dist/src/docs/typescript.md +584 -0
- package/dist/src/docs/vercel-ai-sdk.md +304 -0
- package/dist/src/lib/agent-interface.d.ts +46 -0
- package/dist/src/lib/agent-interface.js +292 -0
- package/dist/src/lib/agent-interface.js.map +1 -0
- package/dist/src/lib/agent-prompts.d.ts +10 -0
- package/dist/src/lib/agent-prompts.js +49 -0
- package/dist/src/lib/agent-prompts.js.map +1 -0
- package/dist/src/lib/config.d.ts +39 -0
- package/dist/src/lib/config.js +549 -0
- package/dist/src/lib/config.js.map +1 -0
- package/dist/src/lib/constants.d.ts +27 -0
- package/dist/src/lib/constants.js +165 -0
- package/dist/src/lib/constants.js.map +1 -0
- package/dist/src/lib/handlers.d.ts +68 -0
- package/dist/src/lib/handlers.js +420 -0
- package/dist/src/lib/handlers.js.map +1 -0
- package/dist/src/lib/integration-testing.d.ts +44 -0
- package/dist/src/lib/integration-testing.js +123 -0
- package/dist/src/lib/integration-testing.js.map +1 -0
- package/dist/src/lib/mcp.d.ts +14 -0
- package/dist/src/lib/mcp.js +134 -0
- package/dist/src/lib/mcp.js.map +1 -0
- package/dist/src/lib/sdk-messages.d.ts +17 -0
- package/dist/src/lib/sdk-messages.js +278 -0
- package/dist/src/lib/sdk-messages.js.map +1 -0
- package/dist/src/lib/wizard.d.ts +6 -0
- package/dist/src/lib/wizard.js +131 -0
- package/dist/src/lib/wizard.js.map +1 -0
- package/dist/src/run.d.ts +8 -0
- package/dist/src/run.js +53 -0
- package/dist/src/run.js.map +1 -0
- package/dist/src/ui/App.d.ts +15 -0
- package/dist/src/ui/App.js +27 -0
- package/dist/src/ui/App.js.map +1 -0
- package/dist/src/ui/cancellation.d.ts +14 -0
- package/dist/src/ui/cancellation.js +17 -0
- package/dist/src/ui/cancellation.js.map +1 -0
- package/dist/src/ui/components/ClarifyingQuestionsPrompt.d.ts +17 -0
- package/dist/src/ui/components/ClarifyingQuestionsPrompt.js +359 -0
- package/dist/src/ui/components/ClarifyingQuestionsPrompt.js.map +1 -0
- package/dist/src/ui/components/ContinuePrompt.d.ts +14 -0
- package/dist/src/ui/components/ContinuePrompt.js +23 -0
- package/dist/src/ui/components/ContinuePrompt.js.map +1 -0
- package/dist/src/ui/components/DiffDisplay.d.ts +18 -0
- package/dist/src/ui/components/DiffDisplay.js +110 -0
- package/dist/src/ui/components/DiffDisplay.js.map +1 -0
- package/dist/src/ui/components/FeedbackSelectPrompt.d.ts +20 -0
- package/dist/src/ui/components/FeedbackSelectPrompt.js +132 -0
- package/dist/src/ui/components/FeedbackSelectPrompt.js.map +1 -0
- package/dist/src/ui/components/HistoryItemDisplay.d.ts +14 -0
- package/dist/src/ui/components/HistoryItemDisplay.js +140 -0
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -0
- package/dist/src/ui/components/Logo.d.ts +10 -0
- package/dist/src/ui/components/Logo.js +47 -0
- package/dist/src/ui/components/Logo.js.map +1 -0
- package/dist/src/ui/components/OrgInfoBox.d.ts +11 -0
- package/dist/src/ui/components/OrgInfoBox.js +16 -0
- package/dist/src/ui/components/OrgInfoBox.js.map +1 -0
- package/dist/src/ui/components/PendingPrompt.d.ts +18 -0
- package/dist/src/ui/components/PendingPrompt.js +57 -0
- package/dist/src/ui/components/PendingPrompt.js.map +1 -0
- package/dist/src/ui/components/PersistentTextInput.d.ts +21 -0
- package/dist/src/ui/components/PersistentTextInput.js +117 -0
- package/dist/src/ui/components/PersistentTextInput.js.map +1 -0
- package/dist/src/ui/components/PlanApprovalPrompt.d.ts +19 -0
- package/dist/src/ui/components/PlanApprovalPrompt.js +62 -0
- package/dist/src/ui/components/PlanApprovalPrompt.js.map +1 -0
- package/dist/src/ui/components/PromptContainer.d.ts +14 -0
- package/dist/src/ui/components/PromptContainer.js +18 -0
- package/dist/src/ui/components/PromptContainer.js.map +1 -0
- package/dist/src/ui/components/SelectPrompt.d.ts +14 -0
- package/dist/src/ui/components/SelectPrompt.js +62 -0
- package/dist/src/ui/components/SelectPrompt.js.map +1 -0
- package/dist/src/ui/components/SpinnerDisplay.d.ts +13 -0
- package/dist/src/ui/components/SpinnerDisplay.js +11 -0
- package/dist/src/ui/components/SpinnerDisplay.js.map +1 -0
- package/dist/src/ui/components/ToolApprovalPrompt.d.ts +14 -0
- package/dist/src/ui/components/ToolApprovalPrompt.js +142 -0
- package/dist/src/ui/components/ToolApprovalPrompt.js.map +1 -0
- package/dist/src/ui/components/ToolCallDisplay.d.ts +14 -0
- package/dist/src/ui/components/ToolCallDisplay.js +83 -0
- package/dist/src/ui/components/ToolCallDisplay.js.map +1 -0
- package/dist/src/ui/components/WriteKeyDisplay.d.ts +15 -0
- package/dist/src/ui/components/WriteKeyDisplay.js +13 -0
- package/dist/src/ui/components/WriteKeyDisplay.js.map +1 -0
- package/dist/src/ui/contexts/WizardContext.d.ts +210 -0
- package/dist/src/ui/contexts/WizardContext.js +362 -0
- package/dist/src/ui/contexts/WizardContext.js.map +1 -0
- package/dist/src/ui/hooks/useCancellation.d.ts +15 -0
- package/dist/src/ui/hooks/useCancellation.js +25 -0
- package/dist/src/ui/hooks/useCancellation.js.map +1 -0
- package/dist/src/ui/render.d.ts +34 -0
- package/dist/src/ui/render.js +94 -0
- package/dist/src/ui/render.js.map +1 -0
- package/dist/src/ui/types.d.ts +184 -0
- package/dist/src/ui/types.js +6 -0
- package/dist/src/ui/types.js.map +1 -0
- package/dist/src/utils/clack-utils.d.ts +13 -0
- package/dist/src/utils/clack-utils.js +131 -0
- package/dist/src/utils/clack-utils.js.map +1 -0
- package/dist/src/utils/debug.d.ts +13 -0
- package/dist/src/utils/debug.js +47 -0
- package/dist/src/utils/debug.js.map +1 -0
- package/dist/src/utils/environment.d.ts +5 -0
- package/dist/src/utils/environment.js +131 -0
- package/dist/src/utils/environment.js.map +1 -0
- package/dist/src/utils/logging.d.ts +9 -0
- package/dist/src/utils/logging.js +38 -0
- package/dist/src/utils/logging.js.map +1 -0
- package/dist/src/utils/oauth.d.ts +12 -0
- package/dist/src/utils/oauth.js +497 -0
- package/dist/src/utils/oauth.js.map +1 -0
- package/dist/src/utils/package-json-types.d.ts +44 -0
- package/dist/src/utils/package-json-types.js +6 -0
- package/dist/src/utils/package-json-types.js.map +1 -0
- package/dist/src/utils/package-json.d.ts +19 -0
- package/dist/src/utils/package-json.js +22 -0
- package/dist/src/utils/package-json.js.map +1 -0
- package/dist/src/utils/session.d.ts +2 -0
- package/dist/src/utils/session.js +87 -0
- package/dist/src/utils/session.js.map +1 -0
- package/dist/src/utils/types.d.ts +61 -0
- package/dist/src/utils/types.js +2 -0
- package/dist/src/utils/types.js.map +1 -0
- package/dist/src/utils/ui.d.ts +120 -0
- package/dist/src/utils/ui.js +164 -0
- package/dist/src/utils/ui.js.map +1 -0
- package/package.json +140 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for ink-based UI components
|
|
3
|
+
* Maintains API compatibility with clack
|
|
4
|
+
*/
|
|
5
|
+
export interface SelectOption<T> {
|
|
6
|
+
value: T;
|
|
7
|
+
label: string;
|
|
8
|
+
hint?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SelectOptions<T> {
|
|
11
|
+
message: string;
|
|
12
|
+
options: Array<SelectOption<T>>;
|
|
13
|
+
initialValue?: T;
|
|
14
|
+
}
|
|
15
|
+
export interface TextOptions {
|
|
16
|
+
message: string;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
defaultValue?: string;
|
|
19
|
+
initialValue?: string;
|
|
20
|
+
validate?: (value: string) => string | void;
|
|
21
|
+
}
|
|
22
|
+
export interface SpinnerInstance {
|
|
23
|
+
start: (msg?: string) => void;
|
|
24
|
+
stop: (msg?: string) => void;
|
|
25
|
+
message: (msg: string) => void;
|
|
26
|
+
}
|
|
27
|
+
export interface LogFunctions {
|
|
28
|
+
info: (msg: string) => void;
|
|
29
|
+
warn: (msg: string) => void;
|
|
30
|
+
error: (msg: string) => void;
|
|
31
|
+
success: (msg: string) => void;
|
|
32
|
+
step: (msg: string) => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Tool approval result - returned to the SDK's canUseTool callback
|
|
36
|
+
*/
|
|
37
|
+
export type ToolApprovalResult = {
|
|
38
|
+
behavior: 'allow';
|
|
39
|
+
updatedInput: Record<string, unknown>;
|
|
40
|
+
allowAllEdits?: boolean;
|
|
41
|
+
allowAllForFile?: string;
|
|
42
|
+
allowAllForCommandPattern?: string;
|
|
43
|
+
} | {
|
|
44
|
+
behavior: 'deny';
|
|
45
|
+
message: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Props for tool approval prompts
|
|
49
|
+
*/
|
|
50
|
+
export interface ToolApprovalProps {
|
|
51
|
+
toolName: string;
|
|
52
|
+
input: Record<string, unknown>;
|
|
53
|
+
/** For file edits, the diff content */
|
|
54
|
+
diffContent?: string;
|
|
55
|
+
/** For file edits, the filename */
|
|
56
|
+
fileName?: string;
|
|
57
|
+
/** Description of the tool action */
|
|
58
|
+
description?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A single question option from the SDK
|
|
62
|
+
*/
|
|
63
|
+
export interface QuestionOption {
|
|
64
|
+
label: string;
|
|
65
|
+
description: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A single clarifying question from the SDK
|
|
69
|
+
*/
|
|
70
|
+
export interface ClarifyingQuestion {
|
|
71
|
+
question: string;
|
|
72
|
+
header: string;
|
|
73
|
+
options: QuestionOption[];
|
|
74
|
+
multiSelect: boolean;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Props for clarifying questions prompts
|
|
78
|
+
*/
|
|
79
|
+
export interface ClarifyingQuestionsProps {
|
|
80
|
+
questions: ClarifyingQuestion[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Result from clarifying questions - returned to the SDK
|
|
84
|
+
*/
|
|
85
|
+
export interface ClarifyingQuestionsResult {
|
|
86
|
+
questions: ClarifyingQuestion[];
|
|
87
|
+
answers: Record<string, string>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Props for plan approval prompts
|
|
91
|
+
*/
|
|
92
|
+
export interface PlanApprovalProps {
|
|
93
|
+
planContent: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Result from plan approval - returned to the handler
|
|
97
|
+
*/
|
|
98
|
+
export interface PlanApprovalResult {
|
|
99
|
+
approved: boolean;
|
|
100
|
+
feedback?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Props for unified text input component.
|
|
104
|
+
* Handles both callback mode (during agent execution) and context mode (standalone prompts).
|
|
105
|
+
* Note: Spinner is managed separately via ui.spinner().
|
|
106
|
+
*/
|
|
107
|
+
export interface PersistentInputProps {
|
|
108
|
+
placeholder?: string;
|
|
109
|
+
/** Message to show above the input */
|
|
110
|
+
message?: string;
|
|
111
|
+
/** Default value for the input */
|
|
112
|
+
defaultValue?: string;
|
|
113
|
+
/** Validation function - return error message or void */
|
|
114
|
+
validate?: (value: string) => string | void;
|
|
115
|
+
/** Callback when user submits - if not provided, uses resolvePending from context */
|
|
116
|
+
onSubmit?: (message: string) => void | Promise<void>;
|
|
117
|
+
/** Callback when user presses Esc - if not provided, uses resolvePending from context */
|
|
118
|
+
onInterrupt?: () => void;
|
|
119
|
+
/** Callback when user presses Ctrl+C - if not provided, falls back to onInterrupt */
|
|
120
|
+
onCtrlC?: () => void;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* An option in a feedback select prompt
|
|
124
|
+
*/
|
|
125
|
+
export interface FeedbackSelectOption<T> {
|
|
126
|
+
value: T;
|
|
127
|
+
label: string;
|
|
128
|
+
/** If true, selecting this option enables inline text input */
|
|
129
|
+
allowTextInput?: boolean;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Props for feedback select prompts
|
|
133
|
+
*/
|
|
134
|
+
export interface FeedbackSelectOptions<T> {
|
|
135
|
+
message: string;
|
|
136
|
+
options: Array<FeedbackSelectOption<T>>;
|
|
137
|
+
/** If true, don't add this prompt's result to history */
|
|
138
|
+
skipHistory?: boolean;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Result from feedback select - either an option was selected or text was entered
|
|
142
|
+
*/
|
|
143
|
+
export type FeedbackSelectResult<T> = {
|
|
144
|
+
type: 'option';
|
|
145
|
+
value: T;
|
|
146
|
+
} | {
|
|
147
|
+
type: 'text';
|
|
148
|
+
value: string;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Props for continue button prompts
|
|
152
|
+
*/
|
|
153
|
+
export interface ContinuePromptOptions {
|
|
154
|
+
message: string;
|
|
155
|
+
buttonLabel?: string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Status of a tool call
|
|
159
|
+
*/
|
|
160
|
+
export type ToolCallStatus = 'pending' | 'executing' | 'success' | 'error' | 'denied' | 'interrupted';
|
|
161
|
+
/**
|
|
162
|
+
* A tool call to display in history
|
|
163
|
+
*/
|
|
164
|
+
export interface ToolCallInfo {
|
|
165
|
+
toolName: string;
|
|
166
|
+
description?: string;
|
|
167
|
+
status: ToolCallStatus;
|
|
168
|
+
input?: Record<string, unknown>;
|
|
169
|
+
result?: string;
|
|
170
|
+
error?: string;
|
|
171
|
+
/** For file edits, the diff content */
|
|
172
|
+
diffContent?: string;
|
|
173
|
+
/** For file edits, the filename */
|
|
174
|
+
fileName?: string;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Handle to control an active agent query
|
|
178
|
+
*/
|
|
179
|
+
export interface AgentQueryHandle {
|
|
180
|
+
/** Interrupt the current agent execution */
|
|
181
|
+
interrupt: () => Promise<void>;
|
|
182
|
+
/** @deprecated No-op - use interrupt() then resume with a new prompt instead */
|
|
183
|
+
sendMessage: (message: string) => void;
|
|
184
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/ui/types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * Type definitions for ink-based UI components\n * Maintains API compatibility with clack\n */\n\nexport interface SelectOption<T> {\n value: T;\n label: string;\n hint?: string;\n}\n\nexport interface SelectOptions<T> {\n message: string;\n options: Array<SelectOption<T>>;\n initialValue?: T;\n}\n\nexport interface TextOptions {\n message: string;\n placeholder?: string;\n defaultValue?: string;\n initialValue?: string;\n validate?: (value: string) => string | void;\n}\n\nexport interface SpinnerInstance {\n start: (msg?: string) => void;\n stop: (msg?: string) => void;\n message: (msg: string) => void;\n}\n\nexport interface LogFunctions {\n info: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n success: (msg: string) => void;\n step: (msg: string) => void;\n}\n\n// ============================================================================\n// Tool Approval Types\n// ============================================================================\n\n/**\n * Tool approval result - returned to the SDK's canUseTool callback\n */\nexport type ToolApprovalResult =\n | { behavior: 'allow'; updatedInput: Record<string, unknown>; allowAllEdits?: boolean; allowAllForFile?: string; allowAllForCommandPattern?: string }\n | { behavior: 'deny'; message: string };\n\n/**\n * Props for tool approval prompts\n */\nexport interface ToolApprovalProps {\n toolName: string;\n input: Record<string, unknown>;\n /** For file edits, the diff content */\n diffContent?: string;\n /** For file edits, the filename */\n fileName?: string;\n /** Description of the tool action */\n description?: string;\n}\n\n// ============================================================================\n// Clarifying Questions Types (AskUserQuestion tool)\n// ============================================================================\n\n/**\n * A single question option from the SDK\n */\nexport interface QuestionOption {\n label: string;\n description: string;\n}\n\n/**\n * A single clarifying question from the SDK\n */\nexport interface ClarifyingQuestion {\n question: string;\n header: string;\n options: QuestionOption[];\n multiSelect: boolean;\n}\n\n/**\n * Props for clarifying questions prompts\n */\nexport interface ClarifyingQuestionsProps {\n questions: ClarifyingQuestion[];\n}\n\n/**\n * Result from clarifying questions - returned to the SDK\n */\nexport interface ClarifyingQuestionsResult {\n questions: ClarifyingQuestion[];\n answers: Record<string, string>;\n}\n\n// ============================================================================\n// Plan Approval Types (ExitPlanMode tool)\n// ============================================================================\n\n/**\n * Props for plan approval prompts\n */\nexport interface PlanApprovalProps {\n planContent: string;\n}\n\n/**\n * Result from plan approval - returned to the handler\n */\nexport interface PlanApprovalResult {\n approved: boolean;\n feedback?: string; // Present only if approved: false\n}\n\n// ============================================================================\n// Persistent Input Types\n// ============================================================================\n\n/**\n * Props for unified text input component.\n * Handles both callback mode (during agent execution) and context mode (standalone prompts).\n * Note: Spinner is managed separately via ui.spinner().\n */\nexport interface PersistentInputProps {\n placeholder?: string;\n /** Message to show above the input */\n message?: string;\n /** Default value for the input */\n defaultValue?: string;\n /** Validation function - return error message or void */\n validate?: (value: string) => string | void;\n /** Callback when user submits - if not provided, uses resolvePending from context */\n onSubmit?: (message: string) => void | Promise<void>;\n /** Callback when user presses Esc - if not provided, uses resolvePending from context */\n onInterrupt?: () => void;\n /** Callback when user presses Ctrl+C - if not provided, falls back to onInterrupt */\n onCtrlC?: () => void;\n}\n\n// ============================================================================\n// Feedback Select Types (Select with inline text input option)\n// ============================================================================\n\n/**\n * An option in a feedback select prompt\n */\nexport interface FeedbackSelectOption<T> {\n value: T;\n label: string;\n /** If true, selecting this option enables inline text input */\n allowTextInput?: boolean;\n}\n\n/**\n * Props for feedback select prompts\n */\nexport interface FeedbackSelectOptions<T> {\n message: string;\n options: Array<FeedbackSelectOption<T>>;\n /** If true, don't add this prompt's result to history */\n skipHistory?: boolean;\n}\n\n/**\n * Result from feedback select - either an option was selected or text was entered\n */\nexport type FeedbackSelectResult<T> =\n | { type: 'option'; value: T }\n | { type: 'text'; value: string };\n\n// ============================================================================\n// Continue Button Types\n// ============================================================================\n\n/**\n * Props for continue button prompts\n */\nexport interface ContinuePromptOptions {\n message: string;\n buttonLabel?: string;\n}\n\n// ============================================================================\n// Tool Call Display Types\n// ============================================================================\n\n/**\n * Status of a tool call\n */\nexport type ToolCallStatus =\n | 'pending'\n | 'executing'\n | 'success'\n | 'error'\n | 'denied'\n | 'interrupted';\n\n/**\n * A tool call to display in history\n */\nexport interface ToolCallInfo {\n toolName: string;\n description?: string;\n status: ToolCallStatus;\n input?: Record<string, unknown>;\n result?: string;\n error?: string;\n /** For file edits, the diff content */\n diffContent?: string;\n /** For file edits, the filename */\n fileName?: string;\n}\n\n// ============================================================================\n// Agent Query Handle\n// ============================================================================\n\n/**\n * Handle to control an active agent query\n */\nexport interface AgentQueryHandle {\n /** Interrupt the current agent execution */\n interrupt: () => Promise<void>;\n /** @deprecated No-op - use interrupt() then resume with a new prompt instead */\n sendMessage: (message: string) => void;\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WizardOptions } from './types.js';
|
|
2
|
+
import type { Integration } from '../lib/constants.js';
|
|
3
|
+
import { type ConsentFlowResult } from './oauth.js';
|
|
4
|
+
export declare function abort(message?: string, status?: number): never;
|
|
5
|
+
export declare function abortIfCancelled<T>(input: T | Promise<T>, integration?: Integration): Promise<Exclude<T, symbol>>;
|
|
6
|
+
export declare function confirmContinueIfNoOrDirtyGitRepo(options: Pick<WizardOptions, 'default'>): Promise<void>;
|
|
7
|
+
export declare function isInGitRepo(): boolean;
|
|
8
|
+
export declare function getUncommittedOrUntrackedFiles(): string[];
|
|
9
|
+
export declare function askForWizardLogin(): Promise<ConsentFlowResult>;
|
|
10
|
+
/**
|
|
11
|
+
* Wait for user to press any key
|
|
12
|
+
*/
|
|
13
|
+
export declare function waitForUserKeyPress(): Promise<void>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as childProcess from 'node:child_process';
|
|
2
|
+
import * as os from 'node:os';
|
|
3
|
+
import Chalk from 'chalk';
|
|
4
|
+
// chalk v2 types don't work well with ESM default imports
|
|
5
|
+
const chalk = Chalk;
|
|
6
|
+
import ui from './ui.js';
|
|
7
|
+
import { INTEGRATION_CONFIG } from '../lib/config.js';
|
|
8
|
+
import { performConsentFlow } from './oauth.js';
|
|
9
|
+
import { debug } from './debug.js';
|
|
10
|
+
export function abort(message, status) {
|
|
11
|
+
ui.addItem({ type: 'outro', text: message ?? 'Wizard setup cancelled.' });
|
|
12
|
+
return process.exit(status ?? 1);
|
|
13
|
+
}
|
|
14
|
+
export async function abortIfCancelled(input, integration) {
|
|
15
|
+
const resolvedInput = await input;
|
|
16
|
+
if (ui.isCancel(resolvedInput) ||
|
|
17
|
+
(typeof resolvedInput === 'symbol' &&
|
|
18
|
+
resolvedInput.description === 'clack:cancel')) {
|
|
19
|
+
const docsUrl = integration
|
|
20
|
+
? INTEGRATION_CONFIG[integration].docsUrl
|
|
21
|
+
: 'https://raindrop.com/docs';
|
|
22
|
+
ui.addItem({
|
|
23
|
+
type: 'cancel',
|
|
24
|
+
text: `Wizard setup cancelled. You can read the documentation for ${integration ?? 'Raindrop'} at ${chalk.cyan(docsUrl)} to continue with the setup manually.`,
|
|
25
|
+
});
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
return resolvedInput;
|
|
29
|
+
}
|
|
30
|
+
export async function confirmContinueIfNoOrDirtyGitRepo(options) {
|
|
31
|
+
if (!isInGitRepo()) {
|
|
32
|
+
const continueWithoutGit = options.default
|
|
33
|
+
? true
|
|
34
|
+
: await abortIfCancelled(ui.select({
|
|
35
|
+
message: 'You are not inside a git repository. The Raindrop wizard will create and update files. Do you want to continue anyway?',
|
|
36
|
+
options: [
|
|
37
|
+
{ label: 'Yes', value: true },
|
|
38
|
+
{ label: 'No', value: false },
|
|
39
|
+
],
|
|
40
|
+
}));
|
|
41
|
+
if (!continueWithoutGit) {
|
|
42
|
+
abort(undefined, 0);
|
|
43
|
+
}
|
|
44
|
+
// return early to avoid checking for uncommitted files
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const uncommittedOrUntrackedFiles = getUncommittedOrUntrackedFiles();
|
|
48
|
+
if (uncommittedOrUntrackedFiles.length) {
|
|
49
|
+
ui.addItem({
|
|
50
|
+
type: 'warning',
|
|
51
|
+
text: `You have uncommitted or untracked files in your repo:
|
|
52
|
+
|
|
53
|
+
${uncommittedOrUntrackedFiles.join('\n')}
|
|
54
|
+
|
|
55
|
+
The Raindrop wizard will create and update files.`,
|
|
56
|
+
});
|
|
57
|
+
const continueWithDirtyRepo = await abortIfCancelled(ui.select({
|
|
58
|
+
message: 'Do you want to continue anyway?',
|
|
59
|
+
options: [
|
|
60
|
+
{ label: 'Yes', value: true },
|
|
61
|
+
{ label: 'No', value: false },
|
|
62
|
+
],
|
|
63
|
+
}));
|
|
64
|
+
if (!continueWithDirtyRepo) {
|
|
65
|
+
abort(undefined, 0);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function isInGitRepo() {
|
|
70
|
+
try {
|
|
71
|
+
childProcess.execSync('git rev-parse --is-inside-work-tree', {
|
|
72
|
+
stdio: 'ignore',
|
|
73
|
+
});
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// Not in a git repo - this is expected in some cases
|
|
78
|
+
debug(`Not in git repository: ${error instanceof Error ? error.message : String(error)}`);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function getUncommittedOrUntrackedFiles() {
|
|
83
|
+
try {
|
|
84
|
+
const gitStatus = childProcess
|
|
85
|
+
.execSync('git status --porcelain=v1', {
|
|
86
|
+
// we only care about stdout
|
|
87
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
88
|
+
})
|
|
89
|
+
.toString();
|
|
90
|
+
const files = gitStatus
|
|
91
|
+
.split(os.EOL)
|
|
92
|
+
.map((line) => line.trim())
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
.map((f) => `- ${f.split(/\s+/)[1]}`);
|
|
95
|
+
return files;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// Error running git status - likely not in a git repo or git not available
|
|
99
|
+
debug(`Failed to get git status: ${error instanceof Error ? error.message : String(error)}`);
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export async function askForWizardLogin() {
|
|
104
|
+
return performConsentFlow();
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Wait for user to press any key
|
|
108
|
+
*/
|
|
109
|
+
export async function waitForUserKeyPress() {
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
const stdin = process.stdin;
|
|
112
|
+
// Check if stdin is a TTY and can be set to raw mode
|
|
113
|
+
if (!stdin.isTTY) {
|
|
114
|
+
// If not a TTY, just resolve immediately (shouldn't happen in normal usage)
|
|
115
|
+
resolve();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const wasRaw = stdin.isRaw;
|
|
119
|
+
stdin.setRawMode(true);
|
|
120
|
+
stdin.resume();
|
|
121
|
+
stdin.setEncoding('utf8');
|
|
122
|
+
const onData = () => {
|
|
123
|
+
stdin.setRawMode(wasRaw);
|
|
124
|
+
stdin.pause();
|
|
125
|
+
stdin.removeListener('data', onData);
|
|
126
|
+
resolve();
|
|
127
|
+
};
|
|
128
|
+
stdin.once('data', onData);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=clack-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clack-utils.js","sourceRoot":"","sources":["../../../src/utils/clack-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import * as childProcess from 'node:child_process';\nimport * as os from 'node:os';\n\nimport Chalk from 'chalk';\n\n// chalk v2 types don't work well with ESM default imports\nconst chalk = Chalk as any;\nimport type { WizardOptions } from './types.js';\nimport type { Integration } from '../lib/constants.js';\nimport ui from './ui.js';\nimport { INTEGRATION_CONFIG } from '../lib/config.js';\nimport { performConsentFlow, type ConsentFlowResult } from './oauth.js';\nimport { debug } from './debug.js';\n\nexport function abort(message?: string, status?: number): never {\n ui.addItem({ type: 'outro', text: message ?? 'Wizard setup cancelled.' });\n return process.exit(status ?? 1);\n}\n\nexport async function abortIfCancelled<T>(\n input: T | Promise<T>,\n integration?: Integration,\n): Promise<Exclude<T, symbol>> {\n const resolvedInput = await input;\n\n if (\n ui.isCancel(resolvedInput) ||\n (typeof resolvedInput === 'symbol' &&\n resolvedInput.description === 'clack:cancel')\n ) {\n const docsUrl = integration\n ? INTEGRATION_CONFIG[integration].docsUrl\n : 'https://raindrop.com/docs';\n\n ui.addItem({\n type: 'cancel',\n text: `Wizard setup cancelled. You can read the documentation for ${\n integration ?? 'Raindrop'\n } at ${chalk.cyan(docsUrl)} to continue with the setup manually.`,\n });\n process.exit(0);\n }\n\n return resolvedInput as Exclude<T, symbol>;\n}\n\nexport async function confirmContinueIfNoOrDirtyGitRepo(\n options: Pick<WizardOptions, 'default'>,\n): Promise<void> {\n if (!isInGitRepo()) {\n const continueWithoutGit = options.default\n ? true\n : await abortIfCancelled(\n ui.select({\n message:\n 'You are not inside a git repository. The Raindrop wizard will create and update files. Do you want to continue anyway?',\n options: [\n { label: 'Yes', value: true },\n { label: 'No', value: false },\n ],\n }),\n );\n\n if (!continueWithoutGit) {\n abort(undefined, 0);\n }\n // return early to avoid checking for uncommitted files\n return;\n }\n\n const uncommittedOrUntrackedFiles = getUncommittedOrUntrackedFiles();\n if (uncommittedOrUntrackedFiles.length) {\n ui.addItem({\n type: 'warning',\n text: `You have uncommitted or untracked files in your repo:\n\n${uncommittedOrUntrackedFiles.join('\\n')}\n\nThe Raindrop wizard will create and update files.`,\n });\n const continueWithDirtyRepo = await abortIfCancelled(\n ui.select({\n message: 'Do you want to continue anyway?',\n options: [\n { label: 'Yes', value: true },\n { label: 'No', value: false },\n ],\n }),\n );\n\n if (!continueWithDirtyRepo) {\n abort(undefined, 0);\n }\n }\n}\n\nexport function isInGitRepo() {\n try {\n childProcess.execSync('git rev-parse --is-inside-work-tree', {\n stdio: 'ignore',\n });\n return true;\n } catch (error) {\n // Not in a git repo - this is expected in some cases\n debug(\n `Not in git repository: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return false;\n }\n}\n\nexport function getUncommittedOrUntrackedFiles(): string[] {\n try {\n const gitStatus = childProcess\n .execSync('git status --porcelain=v1', {\n // we only care about stdout\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString();\n\n const files = gitStatus\n .split(os.EOL)\n .map((line) => line.trim())\n .filter(Boolean)\n .map((f) => `- ${f.split(/\\s+/)[1]}`);\n\n return files;\n } catch (error) {\n // Error running git status - likely not in a git repo or git not available\n debug(\n `Failed to get git status: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return [];\n }\n}\n\n\nexport async function askForWizardLogin(): Promise<ConsentFlowResult> {\n return performConsentFlow();\n}\n\n/**\n * Wait for user to press any key\n */\nexport async function waitForUserKeyPress(): Promise<void> {\n return new Promise((resolve) => {\n const stdin = process.stdin;\n\n // Check if stdin is a TTY and can be set to raw mode\n if (!stdin.isTTY) {\n // If not a TTY, just resolve immediately (shouldn't happen in normal usage)\n resolve();\n return;\n }\n\n const wasRaw = stdin.isRaw;\n\n stdin.setRawMode(true);\n stdin.resume();\n stdin.setEncoding('utf8');\n\n const onData = () => {\n stdin.setRawMode(wasRaw);\n stdin.pause();\n stdin.removeListener('data', onData);\n resolve();\n };\n\n stdin.once('data', onData);\n });\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const LOG_FILE_PATH = "/tmp/raindrop-ai-wizard.log";
|
|
2
|
+
/**
|
|
3
|
+
* Initialize the log file with a run header.
|
|
4
|
+
* Call this at the start of each wizard run.
|
|
5
|
+
*/
|
|
6
|
+
export declare function initLogFile(): void;
|
|
7
|
+
/**
|
|
8
|
+
* Log a message to the file at /tmp/raindrop-ai-wizard.log.
|
|
9
|
+
* Always writes regardless of debug flag.
|
|
10
|
+
*/
|
|
11
|
+
export declare function logToFile(...args: unknown[]): void;
|
|
12
|
+
export declare function debug(...args: unknown[]): void;
|
|
13
|
+
export declare function enableDebugLogs(): void;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import Chalk from 'chalk';
|
|
2
|
+
// chalk v2 types don't work well with ESM default imports
|
|
3
|
+
const chalk = Chalk;
|
|
4
|
+
import { appendFileSync } from 'fs';
|
|
5
|
+
import { prepareMessage } from './logging.js';
|
|
6
|
+
import ui from './ui.js';
|
|
7
|
+
let debugEnabled = false;
|
|
8
|
+
export const LOG_FILE_PATH = '/tmp/raindrop-ai-wizard.log';
|
|
9
|
+
/**
|
|
10
|
+
* Initialize the log file with a run header.
|
|
11
|
+
* Call this at the start of each wizard run.
|
|
12
|
+
*/
|
|
13
|
+
export function initLogFile() {
|
|
14
|
+
const header = `\n${'='.repeat(60)}\nRaindrop wizard Run: ${new Date().toISOString()}\n${'='.repeat(60)}\n`;
|
|
15
|
+
appendFileSync(LOG_FILE_PATH, header);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Log a message to the file at /tmp/raindrop-ai-wizard.log.
|
|
19
|
+
* Always writes regardless of debug flag.
|
|
20
|
+
*/
|
|
21
|
+
export function logToFile(...args) {
|
|
22
|
+
const timestamp = new Date().toISOString();
|
|
23
|
+
const msg = args.map((a) => prepareMessage(a)).join(' ');
|
|
24
|
+
appendFileSync(LOG_FILE_PATH, `[${timestamp}] ${msg}\n`);
|
|
25
|
+
}
|
|
26
|
+
export function debug(...args) {
|
|
27
|
+
if (!debugEnabled) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Don't sanitize debug logs to ui - they're for development
|
|
31
|
+
const msg = args
|
|
32
|
+
.map((a) => {
|
|
33
|
+
if (typeof a === 'string') {
|
|
34
|
+
return a;
|
|
35
|
+
}
|
|
36
|
+
if (a instanceof Error) {
|
|
37
|
+
return `${a.stack || ''}`;
|
|
38
|
+
}
|
|
39
|
+
return JSON.stringify(a, null, '\t');
|
|
40
|
+
})
|
|
41
|
+
.join(' ');
|
|
42
|
+
ui.addItem({ type: 'response', text: chalk.dim(msg) });
|
|
43
|
+
}
|
|
44
|
+
export function enableDebugLogs() {
|
|
45
|
+
debugEnabled = true;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=debug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug.js","sourceRoot":"","sources":["../../../src/utils/debug.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,0DAA0D;AAC1D,MAAM,KAAK,GAAG,KAAY,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,IAAI,YAAY,GAAG,KAAK,CAAC;AAEzB,MAAM,CAAC,MAAM,aAAa,GAAG,6BAA6B,CAAC;AAE3D;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,MAAM,GAAG,KAAK,GAAG,CAAC,MAAM,CAC5B,EAAE,CACH,0BAA0B,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC;IAC3E,cAAc,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,GAAG,IAAe;IAC1C,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,cAAc,CAAC,aAAa,EAAE,IAAI,SAAS,KAAK,GAAG,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAG,IAAe;IACtC,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO;IACT,CAAC;IAED,4DAA4D;IAC5D,MAAM,GAAG,GAAG,IAAI;SACb,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,CAAC,CAAC;QACX,CAAC;QACD,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;YACvB,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QAC5B,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC","sourcesContent":["import Chalk from 'chalk';\n\n// chalk v2 types don't work well with ESM default imports\nconst chalk = Chalk as any;\nimport { appendFileSync } from 'fs';\nimport { prepareMessage } from './logging.js';\nimport ui from './ui.js';\n\nlet debugEnabled = false;\n\nexport const LOG_FILE_PATH = '/tmp/raindrop-ai-wizard.log';\n\n/**\n * Initialize the log file with a run header.\n * Call this at the start of each wizard run.\n */\nexport function initLogFile() {\n const header = `\\n${'='.repeat(\n 60,\n )}\\nRaindrop wizard Run: ${new Date().toISOString()}\\n${'='.repeat(60)}\\n`;\n appendFileSync(LOG_FILE_PATH, header);\n}\n\n/**\n * Log a message to the file at /tmp/raindrop-ai-wizard.log.\n * Always writes regardless of debug flag.\n */\nexport function logToFile(...args: unknown[]) {\n const timestamp = new Date().toISOString();\n const msg = args.map((a) => prepareMessage(a)).join(' ');\n appendFileSync(LOG_FILE_PATH, `[${timestamp}] ${msg}\\n`);\n}\n\nexport function debug(...args: unknown[]) {\n if (!debugEnabled) {\n return;\n }\n\n // Don't sanitize debug logs to ui - they're for development\n const msg = args\n .map((a) => {\n if (typeof a === 'string') {\n return a;\n }\n if (a instanceof Error) {\n return `${a.stack || ''}`;\n }\n return JSON.stringify(a, null, '\\t');\n })\n .join(' ');\n\n ui.addItem({ type: 'response', text: chalk.dim(msg) });\n}\n\nexport function enableDebugLogs() {\n debugEnabled = true;\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WizardOptions } from './types.js';
|
|
2
|
+
export declare function isNonInteractiveEnvironment(): boolean;
|
|
3
|
+
export declare function readEnvironment(): Record<string, unknown>;
|
|
4
|
+
export declare function detectEnvVarPrefix(options: WizardOptions): Promise<string>;
|
|
5
|
+
export declare function saveWriteKeyToEnv(writeKey: string, installDir: string): Promise<void>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import readEnvModule from 'read-env';
|
|
2
|
+
import fg from 'fast-glob';
|
|
3
|
+
import { IS_DEV } from '../lib/constants.js';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
// Handle CJS interop - read-env exports { default: fn }
|
|
7
|
+
const readEnv = typeof readEnvModule === 'function'
|
|
8
|
+
? readEnvModule
|
|
9
|
+
: readEnvModule.default;
|
|
10
|
+
export function isNonInteractiveEnvironment() {
|
|
11
|
+
if (IS_DEV) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
if (!process.stdout.isTTY || !process.stderr.isTTY) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
export function readEnvironment() {
|
|
20
|
+
const result = readEnv('RAINDROP');
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
export async function detectEnvVarPrefix(options) {
|
|
24
|
+
let deps = {};
|
|
25
|
+
const packageJsonPath = path.join(options.installDir, 'package.json');
|
|
26
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
27
|
+
try {
|
|
28
|
+
const packageJsonContent = await fs.promises.readFile(packageJsonPath, 'utf-8');
|
|
29
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
30
|
+
deps = {
|
|
31
|
+
...(packageJson.dependencies || {}),
|
|
32
|
+
...(packageJson.devDependencies || {}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// package.json exists but couldn't be read/parsed - continue without deps
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const has = (name) => name in deps;
|
|
40
|
+
const hasAnyFile = async (patterns) => {
|
|
41
|
+
const matches = await fg(patterns, {
|
|
42
|
+
cwd: options.installDir,
|
|
43
|
+
absolute: false,
|
|
44
|
+
onlyFiles: true,
|
|
45
|
+
ignore: ['**/node_modules/**'],
|
|
46
|
+
});
|
|
47
|
+
return matches.length > 0;
|
|
48
|
+
};
|
|
49
|
+
// --- Next.js
|
|
50
|
+
if (has('next') || (await hasAnyFile(['**/next.config.{js,ts,mjs,cjs}']))) {
|
|
51
|
+
return 'NEXT_PUBLIC_';
|
|
52
|
+
}
|
|
53
|
+
// --- Create React App
|
|
54
|
+
if (has('react-scripts') ||
|
|
55
|
+
has('create-react-app') ||
|
|
56
|
+
(await hasAnyFile(['**/config-overrides.js']))) {
|
|
57
|
+
return 'REACT_APP_';
|
|
58
|
+
}
|
|
59
|
+
// --- Vite (vanilla, TanStack, Solid, etc.)
|
|
60
|
+
// Note: Vite does not need PUBLIC_ but we use it to follow the docs, to improve the chances of an LLM getting it right.
|
|
61
|
+
if (has('vite') || (await hasAnyFile(['**/vite.config.{js,ts,mjs,cjs}']))) {
|
|
62
|
+
return 'VITE_PUBLIC_';
|
|
63
|
+
}
|
|
64
|
+
// --- SvelteKit
|
|
65
|
+
if (has('@sveltejs/kit') ||
|
|
66
|
+
(await hasAnyFile(['**/svelte.config.{js,ts}']))) {
|
|
67
|
+
return 'PUBLIC_';
|
|
68
|
+
}
|
|
69
|
+
// --- TanStack Start (uses Vite)
|
|
70
|
+
if (has('@tanstack/start') ||
|
|
71
|
+
(await hasAnyFile(['**/tanstack.config.{js,ts}']))) {
|
|
72
|
+
return 'VITE_PUBLIC_';
|
|
73
|
+
}
|
|
74
|
+
// --- SolidStart (uses Vite)
|
|
75
|
+
if (has('solid-start') || (await hasAnyFile(['**/solid.config.{js,ts}']))) {
|
|
76
|
+
return 'VITE_PUBLIC_';
|
|
77
|
+
}
|
|
78
|
+
// --- Astro
|
|
79
|
+
if (has('astro') || (await hasAnyFile(['**/astro.config.{js,ts,mjs}']))) {
|
|
80
|
+
return 'PUBLIC_';
|
|
81
|
+
}
|
|
82
|
+
// We default to Vite if we can't detect a specific framework, since it's the most commonly used.
|
|
83
|
+
return 'VITE_PUBLIC_';
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Validates write key format to prevent injection attacks and file corruption.
|
|
87
|
+
* Write keys should be alphanumeric with optional hyphens/underscores, 20-100 chars.
|
|
88
|
+
*
|
|
89
|
+
* @param key - The write key to validate
|
|
90
|
+
* @returns true if the key is valid, false otherwise
|
|
91
|
+
*/
|
|
92
|
+
function validateWriteKey(key) {
|
|
93
|
+
// Write keys should be alphanumeric with optional hyphens/underscores
|
|
94
|
+
// Length: 20-100 characters (reasonable bounds for write keys)
|
|
95
|
+
const WRITE_KEY_PATTERN = /^[a-zA-Z0-9_-]{20,100}$/;
|
|
96
|
+
// Check pattern and ensure no dangerous characters
|
|
97
|
+
return (WRITE_KEY_PATTERN.test(key) &&
|
|
98
|
+
!key.includes('\n') &&
|
|
99
|
+
!key.includes('\r') &&
|
|
100
|
+
!key.includes('\0'));
|
|
101
|
+
}
|
|
102
|
+
export async function saveWriteKeyToEnv(writeKey, installDir) {
|
|
103
|
+
// Sanitize input by trimming and removing dangerous characters
|
|
104
|
+
const sanitizedKey = writeKey.trim().replace(/[\r\n\0]/g, '');
|
|
105
|
+
// Validate the sanitized key
|
|
106
|
+
if (!validateWriteKey(sanitizedKey)) {
|
|
107
|
+
throw new Error('Invalid write key format. Write keys must be 20-100 characters long and contain only alphanumeric characters, hyphens, and underscores.');
|
|
108
|
+
}
|
|
109
|
+
const envPath = path.join(installDir, '.env');
|
|
110
|
+
let envContent = '';
|
|
111
|
+
try {
|
|
112
|
+
envContent = await fs.promises.readFile(envPath, 'utf-8');
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// File doesn't exist, will create it
|
|
116
|
+
}
|
|
117
|
+
// Check if RAINDROP_WRITE_KEY already exists in the file
|
|
118
|
+
if (envContent.includes('RAINDROP_WRITE_KEY=')) {
|
|
119
|
+
// Replace existing key
|
|
120
|
+
envContent = envContent.replace(/RAINDROP_WRITE_KEY=.*/, `RAINDROP_WRITE_KEY=${sanitizedKey}`);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Add new key
|
|
124
|
+
envContent =
|
|
125
|
+
envContent.trim() +
|
|
126
|
+
(envContent ? '\n' : '') +
|
|
127
|
+
`RAINDROP_WRITE_KEY=${sanitizedKey}\n`;
|
|
128
|
+
}
|
|
129
|
+
await fs.promises.writeFile(envPath, envContent, 'utf-8');
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment.js","sourceRoot":"","sources":["../../../src/utils/environment.ts"],"names":[],"mappings":"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","sourcesContent":["import readEnvModule from 'read-env';\nimport type { WizardOptions } from './types.js';\nimport fg from 'fast-glob';\nimport { IS_DEV } from '../lib/constants.js';\nimport fs from 'fs';\nimport path from 'path';\n\n// Handle CJS interop - read-env exports { default: fn }\nconst readEnv =\n typeof readEnvModule === 'function'\n ? readEnvModule\n : (readEnvModule as { default: typeof readEnvModule }).default;\n\nexport function isNonInteractiveEnvironment(): boolean {\n if (IS_DEV) {\n return false;\n }\n\n if (!process.stdout.isTTY || !process.stderr.isTTY) {\n return true;\n }\n\n return false;\n}\n\nexport function readEnvironment(): Record<string, unknown> {\n const result = readEnv('RAINDROP');\n\n return result;\n}\n\nexport async function detectEnvVarPrefix(\n options: WizardOptions,\n): Promise<string> {\n let deps: Record<string, string> = {};\n const packageJsonPath = path.join(options.installDir, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n try {\n const packageJsonContent = await fs.promises.readFile(\n packageJsonPath,\n 'utf-8',\n );\n const packageJson = JSON.parse(packageJsonContent);\n deps = {\n ...(packageJson.dependencies || {}),\n ...(packageJson.devDependencies || {}),\n };\n } catch {\n // package.json exists but couldn't be read/parsed - continue without deps\n }\n }\n\n const has = (name: string) => name in deps;\n const hasAnyFile = async (patterns: string[]) => {\n const matches = await fg(patterns, {\n cwd: options.installDir,\n absolute: false,\n onlyFiles: true,\n ignore: ['**/node_modules/**'],\n });\n return matches.length > 0;\n };\n\n // --- Next.js\n if (has('next') || (await hasAnyFile(['**/next.config.{js,ts,mjs,cjs}']))) {\n return 'NEXT_PUBLIC_';\n }\n\n // --- Create React App\n if (\n has('react-scripts') ||\n has('create-react-app') ||\n (await hasAnyFile(['**/config-overrides.js']))\n ) {\n return 'REACT_APP_';\n }\n\n // --- Vite (vanilla, TanStack, Solid, etc.)\n // Note: Vite does not need PUBLIC_ but we use it to follow the docs, to improve the chances of an LLM getting it right.\n if (has('vite') || (await hasAnyFile(['**/vite.config.{js,ts,mjs,cjs}']))) {\n return 'VITE_PUBLIC_';\n }\n\n // --- SvelteKit\n if (\n has('@sveltejs/kit') ||\n (await hasAnyFile(['**/svelte.config.{js,ts}']))\n ) {\n return 'PUBLIC_';\n }\n\n // --- TanStack Start (uses Vite)\n if (\n has('@tanstack/start') ||\n (await hasAnyFile(['**/tanstack.config.{js,ts}']))\n ) {\n return 'VITE_PUBLIC_';\n }\n\n // --- SolidStart (uses Vite)\n if (has('solid-start') || (await hasAnyFile(['**/solid.config.{js,ts}']))) {\n return 'VITE_PUBLIC_';\n }\n\n // --- Astro\n if (has('astro') || (await hasAnyFile(['**/astro.config.{js,ts,mjs}']))) {\n return 'PUBLIC_';\n }\n\n // We default to Vite if we can't detect a specific framework, since it's the most commonly used.\n return 'VITE_PUBLIC_';\n}\n\n/**\n * Validates write key format to prevent injection attacks and file corruption.\n * Write keys should be alphanumeric with optional hyphens/underscores, 20-100 chars.\n *\n * @param key - The write key to validate\n * @returns true if the key is valid, false otherwise\n */\nfunction validateWriteKey(key: string): boolean {\n // Write keys should be alphanumeric with optional hyphens/underscores\n // Length: 20-100 characters (reasonable bounds for write keys)\n const WRITE_KEY_PATTERN = /^[a-zA-Z0-9_-]{20,100}$/;\n\n // Check pattern and ensure no dangerous characters\n return (\n WRITE_KEY_PATTERN.test(key) &&\n !key.includes('\\n') &&\n !key.includes('\\r') &&\n !key.includes('\\0')\n );\n}\n\nexport async function saveWriteKeyToEnv(\n writeKey: string,\n installDir: string,\n): Promise<void> {\n // Sanitize input by trimming and removing dangerous characters\n const sanitizedKey = writeKey.trim().replace(/[\\r\\n\\0]/g, '');\n\n // Validate the sanitized key\n if (!validateWriteKey(sanitizedKey)) {\n throw new Error(\n 'Invalid write key format. Write keys must be 20-100 characters long and contain only alphanumeric characters, hyphens, and underscores.',\n );\n }\n\n const envPath = path.join(installDir, '.env');\n let envContent = '';\n\n try {\n envContent = await fs.promises.readFile(envPath, 'utf-8');\n } catch {\n // File doesn't exist, will create it\n }\n\n // Check if RAINDROP_WRITE_KEY already exists in the file\n if (envContent.includes('RAINDROP_WRITE_KEY=')) {\n // Replace existing key\n envContent = envContent.replace(\n /RAINDROP_WRITE_KEY=.*/,\n `RAINDROP_WRITE_KEY=${sanitizedKey}`,\n );\n } else {\n // Add new key\n envContent =\n envContent.trim() +\n (envContent ? '\\n' : '') +\n `RAINDROP_WRITE_KEY=${sanitizedKey}\\n`;\n }\n\n await fs.promises.writeFile(envPath, envContent, 'utf-8');\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function prepareMessage(msg: unknown): string;
|
|
2
|
+
export declare function l(msg: string): void;
|
|
3
|
+
export declare function nl(): void;
|
|
4
|
+
export declare function green(msg: string): void;
|
|
5
|
+
export declare function red(msg: string): void;
|
|
6
|
+
export declare function dim(msg: string): void;
|
|
7
|
+
export declare function yellow(msg: string): void;
|
|
8
|
+
export declare function cyan(msg: string): void;
|
|
9
|
+
export declare function debug(msg: any): void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import ChalkModule from 'chalk';
|
|
2
|
+
// chalk v2 types don't work well with ESM default imports
|
|
3
|
+
const Chalk = ChalkModule;
|
|
4
|
+
export function prepareMessage(msg) {
|
|
5
|
+
if (typeof msg === 'string') {
|
|
6
|
+
return msg;
|
|
7
|
+
}
|
|
8
|
+
if (msg instanceof Error) {
|
|
9
|
+
return `${msg.stack || ''}`;
|
|
10
|
+
}
|
|
11
|
+
return JSON.stringify(msg, null, '\t');
|
|
12
|
+
}
|
|
13
|
+
export function l(msg) {
|
|
14
|
+
// eslint-disable-next-line no-console
|
|
15
|
+
console.log(msg);
|
|
16
|
+
}
|
|
17
|
+
export function nl() {
|
|
18
|
+
return l('');
|
|
19
|
+
}
|
|
20
|
+
export function green(msg) {
|
|
21
|
+
return l(Chalk.green(prepareMessage(msg)));
|
|
22
|
+
}
|
|
23
|
+
export function red(msg) {
|
|
24
|
+
return l(Chalk.red(prepareMessage(msg)));
|
|
25
|
+
}
|
|
26
|
+
export function dim(msg) {
|
|
27
|
+
return l(Chalk.dim(prepareMessage(msg)));
|
|
28
|
+
}
|
|
29
|
+
export function yellow(msg) {
|
|
30
|
+
return l(Chalk.yellow(prepareMessage(msg)));
|
|
31
|
+
}
|
|
32
|
+
export function cyan(msg) {
|
|
33
|
+
return l(Chalk.cyan(prepareMessage(msg)));
|
|
34
|
+
}
|
|
35
|
+
export function debug(msg) {
|
|
36
|
+
return l(Chalk.italic.yellow(prepareMessage(msg)));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=logging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging.js","sourceRoot":"","sources":["../../../src/utils/logging.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,OAAO,CAAC;AAEhC,0DAA0D;AAC1D,MAAM,KAAK,GAAG,WAAkB,CAAC;AAEjC,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,OAAO,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,CAAC,CAAC,GAAW;IAC3B,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,EAAE;IAChB,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,GAAW;IAC7B,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,GAAW;IAC7B,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,GAAW;IAChC,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,GAAW;IAC9B,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAQ;IAC5B,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import ChalkModule from 'chalk';\n\n// chalk v2 types don't work well with ESM default imports\nconst Chalk = ChalkModule as any;\n\nexport function prepareMessage(msg: unknown): string {\n if (typeof msg === 'string') {\n return msg;\n }\n if (msg instanceof Error) {\n return `${msg.stack || ''}`;\n }\n return JSON.stringify(msg, null, '\\t');\n}\n\nexport function l(msg: string): void {\n // eslint-disable-next-line no-console\n console.log(msg);\n}\n\nexport function nl(): void {\n return l('');\n}\n\nexport function green(msg: string): void {\n return l(Chalk.green(prepareMessage(msg)));\n}\n\nexport function red(msg: string): void {\n return l(Chalk.red(prepareMessage(msg)));\n}\n\nexport function dim(msg: string): void {\n return l(Chalk.dim(prepareMessage(msg)));\n}\n\nexport function yellow(msg: string): void {\n return l(Chalk.yellow(prepareMessage(msg)));\n}\n\nexport function cyan(msg: string): void {\n return l(Chalk.cyan(prepareMessage(msg)));\n}\n\nexport function debug(msg: any): void {\n return l(Chalk.italic.yellow(prepareMessage(msg)));\n}\n"]}
|