launch-ih 1.0.0
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/README.md +117 -0
- package/dist/browser.d.ts +20 -0
- package/dist/browser.js +86 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli/commands/draft.command.d.ts +10 -0
- package/dist/cli/commands/draft.command.js +32 -0
- package/dist/cli/commands/draft.command.js.map +1 -0
- package/dist/cli/commands/history.command.d.ts +9 -0
- package/dist/cli/commands/history.command.js +30 -0
- package/dist/cli/commands/history.command.js.map +1 -0
- package/dist/cli/commands/login.command.d.ts +10 -0
- package/dist/cli/commands/login.command.js +64 -0
- package/dist/cli/commands/login.command.js.map +1 -0
- package/dist/cli/commands/new.command.d.ts +17 -0
- package/dist/cli/commands/new.command.js +151 -0
- package/dist/cli/commands/new.command.js.map +1 -0
- package/dist/cli/commands/preview.command.d.ts +13 -0
- package/dist/cli/commands/preview.command.js +48 -0
- package/dist/cli/commands/preview.command.js.map +1 -0
- package/dist/cli/commands/publish.command.d.ts +12 -0
- package/dist/cli/commands/publish.command.js +70 -0
- package/dist/cli/commands/publish.command.js.map +1 -0
- package/dist/cli/commands/status.command.d.ts +9 -0
- package/dist/cli/commands/status.command.js +47 -0
- package/dist/cli/commands/status.command.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +168 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/ui/editor.d.ts +1 -0
- package/dist/cli/ui/editor.js +39 -0
- package/dist/cli/ui/editor.js.map +1 -0
- package/dist/cli/ui/output.d.ts +23 -0
- package/dist/cli/ui/output.js +96 -0
- package/dist/cli/ui/output.js.map +1 -0
- package/dist/cli/ui/prompts.d.ts +11 -0
- package/dist/cli/ui/prompts.js +28 -0
- package/dist/cli/ui/prompts.js.map +1 -0
- package/dist/cli/utils/errors.d.ts +20 -0
- package/dist/cli/utils/errors.js +48 -0
- package/dist/cli/utils/errors.js.map +1 -0
- package/dist/cli/utils/session-helpers.d.ts +4 -0
- package/dist/cli/utils/session-helpers.js +37 -0
- package/dist/cli/utils/session-helpers.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -0
- package/dist/env.d.ts +10 -0
- package/dist/env.js +27 -0
- package/dist/env.js.map +1 -0
- package/dist/ih-auth.d.ts +32 -0
- package/dist/ih-auth.js +240 -0
- package/dist/ih-auth.js.map +1 -0
- package/dist/ih-poster.d.ts +21 -0
- package/dist/ih-poster.js +217 -0
- package/dist/ih-poster.js.map +1 -0
- package/dist/launch-workflow.d.ts +17 -0
- package/dist/launch-workflow.js +80 -0
- package/dist/launch-workflow.js.map +1 -0
- package/dist/post-drafter.d.ts +6 -0
- package/dist/post-drafter.js +103 -0
- package/dist/post-drafter.js.map +1 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +21 -0
- package/dist/utils.js +54 -0
- package/dist/utils.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ProductInfo, LaunchSession } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a new launch session.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createSession(product: ProductInfo): LaunchSession;
|
|
6
|
+
/**
|
|
7
|
+
* Update session status and save to disk.
|
|
8
|
+
*/
|
|
9
|
+
export declare function updateSession(session: LaunchSession, updates: Partial<LaunchSession>): LaunchSession;
|
|
10
|
+
/**
|
|
11
|
+
* Load the most recent active session.
|
|
12
|
+
*/
|
|
13
|
+
export declare function loadLatestSession(): LaunchSession | null;
|
|
14
|
+
/**
|
|
15
|
+
* List all saved sessions, newest first.
|
|
16
|
+
*/
|
|
17
|
+
export declare function listSessions(): LaunchSession[];
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// LAUNCH — Session & Workflow Orchestration
|
|
3
|
+
// ============================================================
|
|
4
|
+
import { generateId, saveSession, loadSession } from './utils.js';
|
|
5
|
+
import { existsSync, readdirSync, statSync } from 'fs';
|
|
6
|
+
import { resolve } from 'path';
|
|
7
|
+
const SESSIONS_DIR = resolve(process.cwd(), 'sessions');
|
|
8
|
+
/**
|
|
9
|
+
* Create a new launch session.
|
|
10
|
+
*/
|
|
11
|
+
export function createSession(product) {
|
|
12
|
+
const now = new Date().toISOString();
|
|
13
|
+
const session = {
|
|
14
|
+
sessionId: generateId(),
|
|
15
|
+
status: 'drafting',
|
|
16
|
+
product,
|
|
17
|
+
draft: null,
|
|
18
|
+
postResult: null,
|
|
19
|
+
createdAt: now,
|
|
20
|
+
updatedAt: now,
|
|
21
|
+
};
|
|
22
|
+
saveSession(session);
|
|
23
|
+
return session;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Update session status and save to disk.
|
|
27
|
+
*/
|
|
28
|
+
export function updateSession(session, updates) {
|
|
29
|
+
const updated = {
|
|
30
|
+
...session,
|
|
31
|
+
...updates,
|
|
32
|
+
updatedAt: new Date().toISOString(),
|
|
33
|
+
};
|
|
34
|
+
saveSession(updated);
|
|
35
|
+
return updated;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load the most recent active session.
|
|
39
|
+
*/
|
|
40
|
+
export function loadLatestSession() {
|
|
41
|
+
if (!existsSync(SESSIONS_DIR))
|
|
42
|
+
return null;
|
|
43
|
+
try {
|
|
44
|
+
const files = readdirSync(SESSIONS_DIR).filter(f => f.endsWith('.json'));
|
|
45
|
+
if (files.length === 0)
|
|
46
|
+
return null;
|
|
47
|
+
files.sort((a, b) => {
|
|
48
|
+
const statA = statSync(resolve(SESSIONS_DIR, a));
|
|
49
|
+
const statB = statSync(resolve(SESSIONS_DIR, b));
|
|
50
|
+
return statB.mtimeMs - statA.mtimeMs;
|
|
51
|
+
});
|
|
52
|
+
return loadSession(files[0].replace('.json', ''));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* List all saved sessions, newest first.
|
|
60
|
+
*/
|
|
61
|
+
export function listSessions() {
|
|
62
|
+
if (!existsSync(SESSIONS_DIR))
|
|
63
|
+
return [];
|
|
64
|
+
try {
|
|
65
|
+
const files = readdirSync(SESSIONS_DIR)
|
|
66
|
+
.filter(f => f.endsWith('.json'))
|
|
67
|
+
.sort((a, b) => {
|
|
68
|
+
const statA = statSync(resolve(SESSIONS_DIR, a));
|
|
69
|
+
const statB = statSync(resolve(SESSIONS_DIR, b));
|
|
70
|
+
return statB.mtimeMs - statA.mtimeMs;
|
|
71
|
+
});
|
|
72
|
+
return files
|
|
73
|
+
.map(f => loadSession(f.replace('.json', '')))
|
|
74
|
+
.filter((s) => s !== null);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=launch-workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch-workflow.js","sourceRoot":"","sources":["../src/launch-workflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ProductInfo, IHPostDraft } from './types.js';
|
|
2
|
+
export declare function buildIHSystemPrompt(): string;
|
|
3
|
+
export declare function buildIHUserPrompt(product: ProductInfo): string;
|
|
4
|
+
export declare function parseIHDraft(raw: string, product: ProductInfo, link?: string): IHPostDraft;
|
|
5
|
+
export declare function createIHDraft(product: ProductInfo, title: string, body: string, link?: string): IHPostDraft;
|
|
6
|
+
export declare function fallbackIHDraft(product: ProductInfo): IHPostDraft;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// LAUNCH — Post Drafting (orchestrator-driven, no AI API calls)
|
|
3
|
+
// ============================================================
|
|
4
|
+
// ============================================================
|
|
5
|
+
// Indie Hackers Drafting (orchestrator-driven, no AI calls)
|
|
6
|
+
// ============================================================
|
|
7
|
+
export function buildIHSystemPrompt() {
|
|
8
|
+
return `You are writing a post for Indie Hackers — a community of bootstrapped founders and makers.
|
|
9
|
+
|
|
10
|
+
**Voice & Tone:**
|
|
11
|
+
- Build-in-public vibe: "I built this, here's what I learned"
|
|
12
|
+
- First person, conversational — like writing to other founders in a coffee shop
|
|
13
|
+
- NO marketing language: avoid "game-changer", "revolutionary", "cutting-edge", "disruptive", "seamless", "robust", "best-in-class", "next-gen"
|
|
14
|
+
- Focus on the problem, the build story, and what was learned
|
|
15
|
+
- Be honest about struggles, trade-offs, and things you'd do differently
|
|
16
|
+
- End with an invitation for feedback, questions, or discussion
|
|
17
|
+
- Keep paragraphs short (2-4 sentences) for readability
|
|
18
|
+
- No emojis in the title. The body can use them sparingly if they feel natural
|
|
19
|
+
|
|
20
|
+
**Output format:** Write only the post content. Start with the title on the first line, then a blank line, then the body. Do not wrap the title in quotes or markdown headings — just the raw title text on line 1.`;
|
|
21
|
+
}
|
|
22
|
+
export function buildIHUserPrompt(product) {
|
|
23
|
+
const featuresList = product.features.map(f => `- ${f}`).join('\n');
|
|
24
|
+
let prompt = `Product: ${product.name}
|
|
25
|
+
Tagline: ${product.tagline}
|
|
26
|
+
Problem it solves: ${product.problem}
|
|
27
|
+
Key features:
|
|
28
|
+
${featuresList}
|
|
29
|
+
Link: ${product.link}
|
|
30
|
+
Target audience: ${product.targetAudience}
|
|
31
|
+
Stage: ${product.stage}`;
|
|
32
|
+
if (product.additionalContext) {
|
|
33
|
+
prompt += `\nAdditional context: ${product.additionalContext}`;
|
|
34
|
+
}
|
|
35
|
+
prompt += `\n\nWrite an Indie Hackers post about this product.`;
|
|
36
|
+
return prompt;
|
|
37
|
+
}
|
|
38
|
+
export function parseIHDraft(raw, product, link) {
|
|
39
|
+
const lines = raw.split('\n').filter(l => l.trim());
|
|
40
|
+
let title;
|
|
41
|
+
let body;
|
|
42
|
+
if (lines.length > 1) {
|
|
43
|
+
title = lines[0].replace(/^["'""]|["'""]$/g, '').trim();
|
|
44
|
+
body = lines.slice(1).join('\n').trim();
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
title = `I built ${product.name} — ${product.tagline}`;
|
|
48
|
+
body = raw;
|
|
49
|
+
}
|
|
50
|
+
body = body
|
|
51
|
+
.replace(/^(?:---|\*\*\*|___)\s*$/m, '')
|
|
52
|
+
.replace(/^\s*\n+/, '')
|
|
53
|
+
.trim();
|
|
54
|
+
title = title.replace(/^["'""]|["'""]$/g, '').trim();
|
|
55
|
+
const wordCount = body.split(/\s+/).filter(Boolean).length;
|
|
56
|
+
return {
|
|
57
|
+
title,
|
|
58
|
+
body,
|
|
59
|
+
productName: product.name,
|
|
60
|
+
tone: 'conversational',
|
|
61
|
+
wordCount,
|
|
62
|
+
link,
|
|
63
|
+
generatedAt: new Date().toISOString(),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function createIHDraft(product, title, body, link) {
|
|
67
|
+
const wordCount = body.split(/\s+/).filter(Boolean).length;
|
|
68
|
+
return {
|
|
69
|
+
title,
|
|
70
|
+
body,
|
|
71
|
+
productName: product.name,
|
|
72
|
+
tone: 'conversational',
|
|
73
|
+
wordCount,
|
|
74
|
+
link,
|
|
75
|
+
generatedAt: new Date().toISOString(),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export function fallbackIHDraft(product) {
|
|
79
|
+
const featuresBullets = product.features.map(f => `- ${f}`).join('\n');
|
|
80
|
+
const title = `I built ${product.name} — ${product.tagline}`;
|
|
81
|
+
const body = `Hey Indie Hackers,
|
|
82
|
+
|
|
83
|
+
I've been working on ${product.name} and wanted to share what I built.
|
|
84
|
+
|
|
85
|
+
**The problem:** ${product.problem}
|
|
86
|
+
|
|
87
|
+
**What it does:**
|
|
88
|
+
${featuresBullets}
|
|
89
|
+
|
|
90
|
+
I'd love your thoughts. What do you think?
|
|
91
|
+
${product.link}`;
|
|
92
|
+
const wordCount = body.split(/\s+/).filter(Boolean).length;
|
|
93
|
+
return {
|
|
94
|
+
title,
|
|
95
|
+
body,
|
|
96
|
+
productName: product.name,
|
|
97
|
+
tone: 'conversational',
|
|
98
|
+
wordCount,
|
|
99
|
+
link: product.link,
|
|
100
|
+
generatedAt: new Date().toISOString(),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=post-drafter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-drafter.js","sourceRoot":"","sources":["../src/post-drafter.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface ProductInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
tagline: string;
|
|
4
|
+
problem: string;
|
|
5
|
+
features: string[];
|
|
6
|
+
link: string;
|
|
7
|
+
targetAudience: string;
|
|
8
|
+
stage: 'pre-launch' | 'just-launched' | 'growing';
|
|
9
|
+
additionalContext?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface IHCredentials {
|
|
12
|
+
email: string;
|
|
13
|
+
password: string;
|
|
14
|
+
}
|
|
15
|
+
export interface IHAuthSession {
|
|
16
|
+
loggedIn: boolean;
|
|
17
|
+
cookieFile: string;
|
|
18
|
+
email: string;
|
|
19
|
+
username: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
}
|
|
22
|
+
export interface IHPostDraft {
|
|
23
|
+
title: string;
|
|
24
|
+
body: string;
|
|
25
|
+
productName: string;
|
|
26
|
+
tone: string;
|
|
27
|
+
wordCount: number;
|
|
28
|
+
generatedAt: string;
|
|
29
|
+
link?: string;
|
|
30
|
+
tags?: string[];
|
|
31
|
+
}
|
|
32
|
+
export interface IHPostResult {
|
|
33
|
+
success: boolean;
|
|
34
|
+
postUrl: string | null;
|
|
35
|
+
title: string;
|
|
36
|
+
timestamp: string;
|
|
37
|
+
error?: string;
|
|
38
|
+
screenshotPath?: string;
|
|
39
|
+
dryRun: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface CliResponse<T> {
|
|
42
|
+
success: boolean;
|
|
43
|
+
data: T | null;
|
|
44
|
+
error: string | null;
|
|
45
|
+
}
|
|
46
|
+
export type LaunchStatus = 'drafting' | 'awaiting_approval' | 'posting' | 'done' | 'failed';
|
|
47
|
+
export interface LaunchSession {
|
|
48
|
+
sessionId: string;
|
|
49
|
+
status: LaunchStatus;
|
|
50
|
+
product: ProductInfo;
|
|
51
|
+
draft: IHPostDraft | null;
|
|
52
|
+
postResult: IHPostResult | null;
|
|
53
|
+
createdAt: string;
|
|
54
|
+
updatedAt: string;
|
|
55
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,mCAAmC;AACnC,+DAA+D"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { LaunchSession } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Print structured JSON to stdout for the orchestrator to read.
|
|
4
|
+
*/
|
|
5
|
+
export declare function printJson(data: unknown): void;
|
|
6
|
+
/**
|
|
7
|
+
* Ensure a directory exists, creating it if necessary.
|
|
8
|
+
*/
|
|
9
|
+
export declare function ensureDir(dirPath: string): void;
|
|
10
|
+
/**
|
|
11
|
+
* Generate a simple UUID v4.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateId(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Save launch session to disk.
|
|
16
|
+
*/
|
|
17
|
+
export declare function saveSession(session: LaunchSession): void;
|
|
18
|
+
/**
|
|
19
|
+
* Load launch session from disk.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadSession(sessionId: string): LaunchSession | null;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs';
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Print structured JSON to stdout for the orchestrator to read.
|
|
6
|
+
*/
|
|
7
|
+
export function printJson(data) {
|
|
8
|
+
console.log(JSON.stringify(data));
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Ensure a directory exists, creating it if necessary.
|
|
12
|
+
*/
|
|
13
|
+
export function ensureDir(dirPath) {
|
|
14
|
+
if (!existsSync(dirPath)) {
|
|
15
|
+
mkdirSync(dirPath, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate a simple UUID v4.
|
|
20
|
+
*/
|
|
21
|
+
export function generateId() {
|
|
22
|
+
return randomUUID();
|
|
23
|
+
}
|
|
24
|
+
const SESSIONS_DIR = resolve(process.cwd(), 'sessions');
|
|
25
|
+
/**
|
|
26
|
+
* Save launch session to disk.
|
|
27
|
+
*/
|
|
28
|
+
export function saveSession(session) {
|
|
29
|
+
ensureDir(SESSIONS_DIR);
|
|
30
|
+
const filePath = resolve(SESSIONS_DIR, `${session.sessionId}.json`);
|
|
31
|
+
writeFileSync(filePath, JSON.stringify(session, null, 2));
|
|
32
|
+
}
|
|
33
|
+
const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
34
|
+
/**
|
|
35
|
+
* Load launch session from disk.
|
|
36
|
+
*/
|
|
37
|
+
export function loadSession(sessionId) {
|
|
38
|
+
if (!UUID_V4_REGEX.test(sessionId)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const filePath = resolve(SESSIONS_DIR, `${sessionId}.json`);
|
|
43
|
+
const data = readFileSync(filePath, 'utf-8');
|
|
44
|
+
return JSON.parse(data);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (err instanceof SyntaxError) {
|
|
48
|
+
console.error(`Warning: Session file for '${sessionId}' is corrupted (invalid JSON).`);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAI/B;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAa;IACrC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,UAAU,EAAE,CAAC;AACtB,CAAC;AAED,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;AAExD;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,OAAsB;IAChD,SAAS,CAAC,YAAY,CAAC,CAAC;IACxB,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE,GAAG,OAAO,CAAC,SAAS,OAAO,CAAC,CAAC;IACpE,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,aAAa,GAAG,wEAAwE,CAAC;AAE/F;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE,GAAG,SAAS,OAAO,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;YAC/B,OAAO,CAAC,KAAK,CAAC,8BAA8B,SAAS,gCAAgC,CAAC,CAAC;YACvF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "launch-ih",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Ship your product to Indie Hackers from the terminal",
|
|
5
|
+
"bin": {
|
|
6
|
+
"launch": "dist/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": ""
|
|
19
|
+
},
|
|
20
|
+
"homepage": "",
|
|
21
|
+
"bugs": "",
|
|
22
|
+
"keywords": [
|
|
23
|
+
"indie-hackers",
|
|
24
|
+
"cli",
|
|
25
|
+
"launch",
|
|
26
|
+
"product-launch",
|
|
27
|
+
"indie"
|
|
28
|
+
],
|
|
29
|
+
"author": "",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"scripts": {
|
|
32
|
+
"prepublishOnly": "npm run build && npm test",
|
|
33
|
+
"build": "tsc",
|
|
34
|
+
"start": "node dist/cli.js",
|
|
35
|
+
"dev": "tsx src/cli.ts",
|
|
36
|
+
"test": "vitest run"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@inquirer/prompts": "^8.5.2",
|
|
40
|
+
"chalk": "^5.6.2",
|
|
41
|
+
"commander": "^12.1.0",
|
|
42
|
+
"dotenv": "^16.4.0",
|
|
43
|
+
"ora": "^8.2.0",
|
|
44
|
+
"playwright": "^1.45.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^20.0.0",
|
|
48
|
+
"tsx": "^4.16.0",
|
|
49
|
+
"typescript": "^5.5.0",
|
|
50
|
+
"vitest": "^1.6.0"
|
|
51
|
+
}
|
|
52
|
+
}
|