stagent 0.1.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/LICENSE +191 -0
- package/README.md +399 -0
- package/components.json +21 -0
- package/dist/cli.js +171 -0
- package/drizzle.config.ts +12 -0
- package/next.config.mjs +15 -0
- package/package.json +114 -0
- package/postcss.config.mjs +8 -0
- package/public/icon-512.png +0 -0
- package/public/icon.svg +13 -0
- package/public/readme/home-workspace.png +0 -0
- package/public/readme/inbox-approvals.png +0 -0
- package/public/readme/workflow-blueprints.png +0 -0
- package/public/stagent-s-128.png +0 -0
- package/public/stagent-s-64.png +0 -0
- package/src/app/api/blueprints/[id]/instantiate/route.ts +27 -0
- package/src/app/api/blueprints/[id]/route.ts +39 -0
- package/src/app/api/blueprints/import/route.ts +68 -0
- package/src/app/api/blueprints/route.ts +29 -0
- package/src/app/api/command-palette/recent/route.ts +31 -0
- package/src/app/api/data/clear/route.ts +22 -0
- package/src/app/api/data/seed/route.ts +22 -0
- package/src/app/api/documents/[id]/file/route.ts +44 -0
- package/src/app/api/documents/[id]/route.ts +123 -0
- package/src/app/api/documents/route.ts +59 -0
- package/src/app/api/logs/stream/route.ts +101 -0
- package/src/app/api/notifications/[id]/route.ts +36 -0
- package/src/app/api/notifications/mark-all-read/route.ts +13 -0
- package/src/app/api/notifications/pending-approvals/route.ts +10 -0
- package/src/app/api/notifications/pending-approvals/stream/route.ts +101 -0
- package/src/app/api/notifications/route.ts +34 -0
- package/src/app/api/permissions/route.ts +46 -0
- package/src/app/api/profiles/[id]/route.ts +79 -0
- package/src/app/api/profiles/[id]/test/route.ts +42 -0
- package/src/app/api/profiles/import/route.ts +108 -0
- package/src/app/api/profiles/route.ts +50 -0
- package/src/app/api/projects/[id]/route.ts +72 -0
- package/src/app/api/projects/route.ts +53 -0
- package/src/app/api/schedules/[id]/route.ts +185 -0
- package/src/app/api/schedules/route.ts +117 -0
- package/src/app/api/settings/budgets/route.ts +24 -0
- package/src/app/api/settings/openai/route.ts +24 -0
- package/src/app/api/settings/route.ts +21 -0
- package/src/app/api/settings/test/route.ts +26 -0
- package/src/app/api/tasks/[id]/cancel/route.ts +21 -0
- package/src/app/api/tasks/[id]/execute/route.ts +90 -0
- package/src/app/api/tasks/[id]/logs/route.ts +95 -0
- package/src/app/api/tasks/[id]/output/route.ts +47 -0
- package/src/app/api/tasks/[id]/respond/route.ts +64 -0
- package/src/app/api/tasks/[id]/resume/route.ts +76 -0
- package/src/app/api/tasks/[id]/route.ts +77 -0
- package/src/app/api/tasks/assist/route.ts +35 -0
- package/src/app/api/tasks/route.ts +82 -0
- package/src/app/api/uploads/[id]/route.ts +81 -0
- package/src/app/api/uploads/cleanup/route.ts +7 -0
- package/src/app/api/uploads/route.ts +66 -0
- package/src/app/api/workflows/[id]/execute/route.ts +82 -0
- package/src/app/api/workflows/[id]/route.ts +133 -0
- package/src/app/api/workflows/[id]/status/route.ts +54 -0
- package/src/app/api/workflows/[id]/steps/[stepId]/retry/route.ts +22 -0
- package/src/app/api/workflows/route.ts +61 -0
- package/src/app/apple-icon.tsx +31 -0
- package/src/app/costs/page.tsx +256 -0
- package/src/app/dashboard/page.tsx +44 -0
- package/src/app/documents/[id]/page.tsx +46 -0
- package/src/app/documents/page.tsx +45 -0
- package/src/app/error.tsx +26 -0
- package/src/app/global-error.tsx +23 -0
- package/src/app/globals.css +733 -0
- package/src/app/icon.tsx +30 -0
- package/src/app/inbox/loading.tsx +15 -0
- package/src/app/inbox/page.tsx +35 -0
- package/src/app/layout.tsx +78 -0
- package/src/app/manifest.ts +32 -0
- package/src/app/monitor/page.tsx +37 -0
- package/src/app/page.tsx +162 -0
- package/src/app/profiles/[id]/edit/page.tsx +39 -0
- package/src/app/profiles/[id]/page.tsx +33 -0
- package/src/app/profiles/new/page.tsx +22 -0
- package/src/app/profiles/page.tsx +19 -0
- package/src/app/projects/[id]/page.tsx +134 -0
- package/src/app/projects/loading.tsx +17 -0
- package/src/app/projects/page.tsx +32 -0
- package/src/app/schedules/[id]/page.tsx +47 -0
- package/src/app/schedules/page.tsx +18 -0
- package/src/app/settings/loading.tsx +24 -0
- package/src/app/settings/page.tsx +27 -0
- package/src/app/tasks/[id]/page.tsx +45 -0
- package/src/app/tasks/new/page.tsx +27 -0
- package/src/app/workflows/[id]/edit/page.tsx +66 -0
- package/src/app/workflows/[id]/page.tsx +37 -0
- package/src/app/workflows/blueprints/[id]/page.tsx +40 -0
- package/src/app/workflows/blueprints/new/page.tsx +20 -0
- package/src/app/workflows/blueprints/page.tsx +11 -0
- package/src/app/workflows/new/page.tsx +36 -0
- package/src/app/workflows/page.tsx +18 -0
- package/src/components/charts/donut-ring.tsx +64 -0
- package/src/components/charts/mini-bar.tsx +75 -0
- package/src/components/charts/sparkline.tsx +107 -0
- package/src/components/costs/cost-dashboard.tsx +877 -0
- package/src/components/costs/cost-filters.tsx +179 -0
- package/src/components/dashboard/activity-feed.tsx +95 -0
- package/src/components/dashboard/greeting.tsx +30 -0
- package/src/components/dashboard/priority-queue.tsx +79 -0
- package/src/components/dashboard/quick-actions.tsx +62 -0
- package/src/components/dashboard/recent-projects.tsx +79 -0
- package/src/components/dashboard/stats-cards.tsx +114 -0
- package/src/components/documents/document-browser.tsx +235 -0
- package/src/components/documents/document-detail-view.tsx +367 -0
- package/src/components/documents/document-grid.tsx +78 -0
- package/src/components/documents/document-preview.tsx +68 -0
- package/src/components/documents/document-table.tsx +119 -0
- package/src/components/documents/document-upload-dialog.tsx +153 -0
- package/src/components/documents/types.ts +6 -0
- package/src/components/documents/utils.ts +57 -0
- package/src/components/monitoring/connection-indicator.tsx +14 -0
- package/src/components/monitoring/log-entry.tsx +79 -0
- package/src/components/monitoring/log-filters.tsx +57 -0
- package/src/components/monitoring/log-stream.tsx +144 -0
- package/src/components/monitoring/monitor-overview-wrapper.tsx +64 -0
- package/src/components/monitoring/monitor-overview.tsx +119 -0
- package/src/components/notifications/failure-action.tsx +38 -0
- package/src/components/notifications/inbox-list.tsx +165 -0
- package/src/components/notifications/message-response.tsx +196 -0
- package/src/components/notifications/notification-item.tsx +250 -0
- package/src/components/notifications/pending-approval-host.tsx +478 -0
- package/src/components/notifications/permission-action.tsx +37 -0
- package/src/components/notifications/permission-response-actions.tsx +126 -0
- package/src/components/notifications/unread-badge.tsx +35 -0
- package/src/components/profiles/profile-browser.tsx +117 -0
- package/src/components/profiles/profile-card.tsx +78 -0
- package/src/components/profiles/profile-detail-view.tsx +564 -0
- package/src/components/profiles/profile-form-view.tsx +480 -0
- package/src/components/profiles/profile-import-dialog.tsx +113 -0
- package/src/components/projects/project-card.tsx +58 -0
- package/src/components/projects/project-create-dialog.tsx +140 -0
- package/src/components/projects/project-detail.tsx +68 -0
- package/src/components/projects/project-edit-dialog.tsx +219 -0
- package/src/components/projects/project-list.tsx +108 -0
- package/src/components/schedules/schedule-create-dialog.tsx +403 -0
- package/src/components/schedules/schedule-detail-view.tsx +274 -0
- package/src/components/schedules/schedule-list.tsx +242 -0
- package/src/components/schedules/schedule-status-badge.tsx +16 -0
- package/src/components/settings/api-key-form.tsx +141 -0
- package/src/components/settings/auth-config-section.tsx +141 -0
- package/src/components/settings/auth-method-selector.tsx +67 -0
- package/src/components/settings/auth-status-badge.tsx +40 -0
- package/src/components/settings/auth-status-dot.tsx +59 -0
- package/src/components/settings/budget-guardrails-section.tsx +842 -0
- package/src/components/settings/data-management-section.tsx +141 -0
- package/src/components/settings/openai-runtime-section.tsx +104 -0
- package/src/components/settings/permissions-section.tsx +91 -0
- package/src/components/shared/app-sidebar.tsx +123 -0
- package/src/components/shared/card-skeleton.tsx +42 -0
- package/src/components/shared/command-palette.tsx +250 -0
- package/src/components/shared/confirm-dialog.tsx +52 -0
- package/src/components/shared/empty-state.tsx +24 -0
- package/src/components/shared/error-state.tsx +32 -0
- package/src/components/shared/form-section-card.tsx +33 -0
- package/src/components/shared/section-heading.tsx +14 -0
- package/src/components/shared/stagent-logo.tsx +21 -0
- package/src/components/shared/theme-toggle.tsx +46 -0
- package/src/components/tasks/ai-assist-panel.tsx +210 -0
- package/src/components/tasks/content-preview.tsx +89 -0
- package/src/components/tasks/empty-board.tsx +12 -0
- package/src/components/tasks/file-upload.tsx +120 -0
- package/src/components/tasks/kanban-board.tsx +275 -0
- package/src/components/tasks/kanban-column.tsx +75 -0
- package/src/components/tasks/skeleton-board.tsx +21 -0
- package/src/components/tasks/task-attachments.tsx +114 -0
- package/src/components/tasks/task-card.tsx +101 -0
- package/src/components/tasks/task-create-panel.tsx +360 -0
- package/src/components/tasks/task-detail-view.tsx +356 -0
- package/src/components/ui/alert-dialog.tsx +196 -0
- package/src/components/ui/badge.tsx +50 -0
- package/src/components/ui/button.tsx +71 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/dialog.tsx +158 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/popover.tsx +89 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/scroll-area.tsx +58 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +143 -0
- package/src/components/ui/sidebar.tsx +726 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +63 -0
- package/src/components/ui/sonner.tsx +36 -0
- package/src/components/ui/switch.tsx +35 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +57 -0
- package/src/components/workflows/blueprint-editor.tsx +109 -0
- package/src/components/workflows/blueprint-gallery.tsx +155 -0
- package/src/components/workflows/blueprint-preview.tsx +240 -0
- package/src/components/workflows/loop-status-view.tsx +272 -0
- package/src/components/workflows/swarm-dashboard.tsx +185 -0
- package/src/components/workflows/workflow-form-view.tsx +1376 -0
- package/src/components/workflows/workflow-list.tsx +230 -0
- package/src/components/workflows/workflow-status-view.tsx +477 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/instrumentation.ts +7 -0
- package/src/lib/agents/claude-agent.ts +737 -0
- package/src/lib/agents/execution-manager.ts +27 -0
- package/src/lib/agents/profiles/assignment-validation.ts +75 -0
- package/src/lib/agents/profiles/builtins/code-reviewer/SKILL.md +21 -0
- package/src/lib/agents/profiles/builtins/code-reviewer/profile.yaml +28 -0
- package/src/lib/agents/profiles/builtins/data-analyst/SKILL.md +25 -0
- package/src/lib/agents/profiles/builtins/data-analyst/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/devops-engineer/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/devops-engineer/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/document-writer/SKILL.md +16 -0
- package/src/lib/agents/profiles/builtins/document-writer/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/general/SKILL.md +13 -0
- package/src/lib/agents/profiles/builtins/general/profile.yaml +18 -0
- package/src/lib/agents/profiles/builtins/health-fitness-coach/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/health-fitness-coach/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/learning-coach/SKILL.md +35 -0
- package/src/lib/agents/profiles/builtins/learning-coach/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/project-manager/SKILL.md +26 -0
- package/src/lib/agents/profiles/builtins/project-manager/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/researcher/SKILL.md +15 -0
- package/src/lib/agents/profiles/builtins/researcher/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/technical-writer/SKILL.md +31 -0
- package/src/lib/agents/profiles/builtins/technical-writer/profile.yaml +29 -0
- package/src/lib/agents/profiles/builtins/travel-planner/SKILL.md +23 -0
- package/src/lib/agents/profiles/builtins/travel-planner/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/wealth-manager/SKILL.md +24 -0
- package/src/lib/agents/profiles/builtins/wealth-manager/profile.yaml +26 -0
- package/src/lib/agents/profiles/compatibility.ts +109 -0
- package/src/lib/agents/profiles/registry.ts +293 -0
- package/src/lib/agents/profiles/test-runner.ts +18 -0
- package/src/lib/agents/profiles/test-types.ts +20 -0
- package/src/lib/agents/profiles/types.ts +43 -0
- package/src/lib/agents/router.ts +56 -0
- package/src/lib/agents/runtime/catalog.ts +85 -0
- package/src/lib/agents/runtime/claude-sdk.ts +12 -0
- package/src/lib/agents/runtime/claude.ts +370 -0
- package/src/lib/agents/runtime/codex-app-server-client.ts +289 -0
- package/src/lib/agents/runtime/index.ts +167 -0
- package/src/lib/agents/runtime/openai-codex.ts +1089 -0
- package/src/lib/agents/runtime/task-assist-types.ts +8 -0
- package/src/lib/agents/runtime/types.ts +30 -0
- package/src/lib/constants/settings.ts +13 -0
- package/src/lib/constants/status-colors.ts +44 -0
- package/src/lib/constants/task-status.ts +49 -0
- package/src/lib/data/clear.ts +63 -0
- package/src/lib/data/seed-data/documents.ts +715 -0
- package/src/lib/data/seed-data/logs.ts +195 -0
- package/src/lib/data/seed-data/notifications.ts +141 -0
- package/src/lib/data/seed-data/profiles.ts +175 -0
- package/src/lib/data/seed-data/projects.ts +61 -0
- package/src/lib/data/seed-data/schedules.ts +108 -0
- package/src/lib/data/seed-data/tasks.ts +341 -0
- package/src/lib/data/seed-data/usage-ledger.ts +130 -0
- package/src/lib/data/seed-data/workflows.ts +213 -0
- package/src/lib/data/seed.ts +129 -0
- package/src/lib/db/index.ts +221 -0
- package/src/lib/db/migrations/0000_aromatic_gargoyle.sql +59 -0
- package/src/lib/db/migrations/0001_first_iron_patriot.sql +6 -0
- package/src/lib/db/migrations/0002_add_resume_count.sql +1 -0
- package/src/lib/db/migrations/0003_add_settings.sql +5 -0
- package/src/lib/db/migrations/0004_add_documents.sql +20 -0
- package/src/lib/db/migrations/0005_add_document_preprocessing.sql +4 -0
- package/src/lib/db/migrations/0006_add_agent_profile.sql +2 -0
- package/src/lib/db/migrations/0007_add_usage_metering_ledger.sql +30 -0
- package/src/lib/db/migrations/0008_add_document_version.sql +1 -0
- package/src/lib/db/migrations/meta/0000_snapshot.json +416 -0
- package/src/lib/db/migrations/meta/0001_snapshot.json +461 -0
- package/src/lib/db/migrations/meta/0002_snapshot.json +469 -0
- package/src/lib/db/migrations/meta/_journal.json +27 -0
- package/src/lib/db/schema.ts +227 -0
- package/src/lib/documents/cleanup.ts +50 -0
- package/src/lib/documents/context-builder.ts +75 -0
- package/src/lib/documents/output-scanner.ts +166 -0
- package/src/lib/documents/processor.ts +120 -0
- package/src/lib/documents/processors/image.ts +21 -0
- package/src/lib/documents/processors/office.ts +36 -0
- package/src/lib/documents/processors/pdf.ts +12 -0
- package/src/lib/documents/processors/spreadsheet.ts +18 -0
- package/src/lib/documents/processors/text.ts +8 -0
- package/src/lib/documents/registry.ts +25 -0
- package/src/lib/notifications/actionable.ts +108 -0
- package/src/lib/notifications/permissions.ts +169 -0
- package/src/lib/queries/chart-data.ts +184 -0
- package/src/lib/schedules/interval-parser.ts +110 -0
- package/src/lib/schedules/scheduler.ts +220 -0
- package/src/lib/settings/auth.ts +98 -0
- package/src/lib/settings/budget-guardrails.ts +590 -0
- package/src/lib/settings/helpers.ts +23 -0
- package/src/lib/settings/openai-auth.ts +80 -0
- package/src/lib/settings/permissions.ts +102 -0
- package/src/lib/usage/ledger.ts +489 -0
- package/src/lib/usage/pricing.ts +68 -0
- package/src/lib/utils/crypto.ts +90 -0
- package/src/lib/utils/format-timestamp.ts +46 -0
- package/src/lib/utils/session-cleanup.ts +26 -0
- package/src/lib/utils/stagent-paths.ts +18 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/validators/blueprint.ts +43 -0
- package/src/lib/validators/profile.ts +64 -0
- package/src/lib/validators/project.ts +17 -0
- package/src/lib/validators/settings.ts +57 -0
- package/src/lib/validators/task.ts +30 -0
- package/src/lib/workflows/blueprints/builtins/code-review-pipeline.yaml +72 -0
- package/src/lib/workflows/blueprints/builtins/documentation-generation.yaml +62 -0
- package/src/lib/workflows/blueprints/builtins/investment-research.yaml +81 -0
- package/src/lib/workflows/blueprints/builtins/meal-planning.yaml +73 -0
- package/src/lib/workflows/blueprints/builtins/product-research.yaml +72 -0
- package/src/lib/workflows/blueprints/builtins/research-report.yaml +77 -0
- package/src/lib/workflows/blueprints/builtins/sprint-planning.yaml +77 -0
- package/src/lib/workflows/blueprints/builtins/travel-planning.yaml +80 -0
- package/src/lib/workflows/blueprints/instantiator.ts +131 -0
- package/src/lib/workflows/blueprints/registry.ts +128 -0
- package/src/lib/workflows/blueprints/template.ts +58 -0
- package/src/lib/workflows/blueprints/types.ts +38 -0
- package/src/lib/workflows/definition-validation.ts +121 -0
- package/src/lib/workflows/engine.ts +1113 -0
- package/src/lib/workflows/loop-executor.ts +270 -0
- package/src/lib/workflows/parallel.ts +55 -0
- package/src/lib/workflows/swarm.ts +97 -0
- package/src/lib/workflows/types.ts +112 -0
- package/tsconfig.json +41 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
import { writeFileSync, mkdirSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
|
|
5
|
+
const uploadsDir = join(
|
|
6
|
+
process.env.STAGENT_DATA_DIR || join(homedir(), ".stagent"),
|
|
7
|
+
"uploads"
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
export interface DocumentSeed {
|
|
11
|
+
id: string;
|
|
12
|
+
taskId: string;
|
|
13
|
+
projectId: string;
|
|
14
|
+
filename: string;
|
|
15
|
+
originalName: string;
|
|
16
|
+
mimeType: string;
|
|
17
|
+
size: number;
|
|
18
|
+
storagePath: string;
|
|
19
|
+
direction: "input";
|
|
20
|
+
status: "uploaded";
|
|
21
|
+
createdAt: Date;
|
|
22
|
+
updatedAt: Date;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface DocumentDef {
|
|
26
|
+
originalName: string;
|
|
27
|
+
mimeType: string;
|
|
28
|
+
projectIndex: number;
|
|
29
|
+
taskIndex: number;
|
|
30
|
+
content: string | (() => Promise<Buffer>);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// --- Binary file generators ---
|
|
34
|
+
|
|
35
|
+
/** Create a minimal valid DOCX (Open XML) using JSZip */
|
|
36
|
+
async function createDocx(textContent: string): Promise<Buffer> {
|
|
37
|
+
const JSZip = (await import("jszip")).default;
|
|
38
|
+
const zip = new JSZip();
|
|
39
|
+
|
|
40
|
+
zip.file(
|
|
41
|
+
"[Content_Types].xml",
|
|
42
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
43
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
44
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
45
|
+
<Default Extension="xml" ContentType="application/xml"/>
|
|
46
|
+
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
|
47
|
+
</Types>`
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
zip.file(
|
|
51
|
+
"_rels/.rels",
|
|
52
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
53
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
54
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
|
|
55
|
+
</Relationships>`
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const paragraphs = textContent
|
|
59
|
+
.split("\n")
|
|
60
|
+
.map(
|
|
61
|
+
(line) =>
|
|
62
|
+
`<w:p><w:r><w:t xml:space="preserve">${escapeXml(line)}</w:t></w:r></w:p>`
|
|
63
|
+
)
|
|
64
|
+
.join("");
|
|
65
|
+
|
|
66
|
+
zip.file(
|
|
67
|
+
"word/document.xml",
|
|
68
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
69
|
+
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
70
|
+
<w:body>${paragraphs}</w:body>
|
|
71
|
+
</w:document>`
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const buf = await zip.generateAsync({ type: "nodebuffer" });
|
|
75
|
+
return Buffer.from(buf);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Create a minimal valid PPTX using JSZip */
|
|
79
|
+
async function createPptx(slides: string[]): Promise<Buffer> {
|
|
80
|
+
const JSZip = (await import("jszip")).default;
|
|
81
|
+
const zip = new JSZip();
|
|
82
|
+
|
|
83
|
+
const slideOverrides = slides
|
|
84
|
+
.map(
|
|
85
|
+
(_, i) =>
|
|
86
|
+
`<Override PartName="/ppt/slides/slide${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>`
|
|
87
|
+
)
|
|
88
|
+
.join("");
|
|
89
|
+
|
|
90
|
+
zip.file(
|
|
91
|
+
"[Content_Types].xml",
|
|
92
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
93
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
94
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
95
|
+
<Default Extension="xml" ContentType="application/xml"/>
|
|
96
|
+
<Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
|
|
97
|
+
${slideOverrides}
|
|
98
|
+
</Types>`
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
zip.file(
|
|
102
|
+
"_rels/.rels",
|
|
103
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
104
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
105
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
|
|
106
|
+
</Relationships>`
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const slideRels = slides
|
|
110
|
+
.map(
|
|
111
|
+
(_, i) =>
|
|
112
|
+
`<Relationship Id="rId${i + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide${i + 1}.xml"/>`
|
|
113
|
+
)
|
|
114
|
+
.join("");
|
|
115
|
+
|
|
116
|
+
zip.file(
|
|
117
|
+
"ppt/_rels/presentation.xml.rels",
|
|
118
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
119
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
120
|
+
${slideRels}
|
|
121
|
+
</Relationships>`
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const slideIdList = slides
|
|
125
|
+
.map((_, i) => `<p:sldId id="${256 + i}" r:id="rId${i + 1}"/>`)
|
|
126
|
+
.join("");
|
|
127
|
+
|
|
128
|
+
zip.file(
|
|
129
|
+
"ppt/presentation.xml",
|
|
130
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
131
|
+
<p:presentation xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
|
|
132
|
+
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
133
|
+
<p:sldIdLst>${slideIdList}</p:sldIdLst>
|
|
134
|
+
</p:presentation>`
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
slides.forEach((text, i) => {
|
|
138
|
+
zip.file(
|
|
139
|
+
`ppt/slides/slide${i + 1}.xml`,
|
|
140
|
+
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
141
|
+
<p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
|
|
142
|
+
xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
|
|
143
|
+
<p:cSld>
|
|
144
|
+
<p:spTree>
|
|
145
|
+
<p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
|
|
146
|
+
<p:grpSpPr/>
|
|
147
|
+
<p:sp>
|
|
148
|
+
<p:nvSpPr><p:cNvPr id="2" name="Title"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr>
|
|
149
|
+
<p:spPr/>
|
|
150
|
+
<p:txBody>
|
|
151
|
+
<a:bodyPr/>
|
|
152
|
+
<a:p><a:r><a:t>${escapeXml(text)}</a:t></a:r></a:p>
|
|
153
|
+
</p:txBody>
|
|
154
|
+
</p:sp>
|
|
155
|
+
</p:spTree>
|
|
156
|
+
</p:cSld>
|
|
157
|
+
</p:sld>`
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const buf = await zip.generateAsync({ type: "nodebuffer" });
|
|
162
|
+
return Buffer.from(buf);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Create a valid XLSX using the xlsx library */
|
|
166
|
+
function createXlsxSync(csvContent: string): Buffer {
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
168
|
+
const XLSX = require("xlsx");
|
|
169
|
+
const rows = csvContent.split("\n").map((line: string) => line.split(","));
|
|
170
|
+
const ws = XLSX.utils.aoa_to_sheet(rows);
|
|
171
|
+
const wb = XLSX.utils.book_new();
|
|
172
|
+
XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
|
|
173
|
+
return XLSX.write(wb, { type: "buffer", bookType: "xlsx" }) as Buffer;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Create a minimal valid PDF with text content */
|
|
177
|
+
function createPdfSync(textContent: string): Buffer {
|
|
178
|
+
const lines = textContent.split("\n");
|
|
179
|
+
const textOps = lines
|
|
180
|
+
.map(
|
|
181
|
+
(line, i) =>
|
|
182
|
+
`1 0 0 1 50 ${750 - i * 14} Tm (${escapePdf(line)}) Tj`
|
|
183
|
+
)
|
|
184
|
+
.join("\n");
|
|
185
|
+
|
|
186
|
+
const stream = `BT\n/F1 10 Tf\n${textOps}\nET`;
|
|
187
|
+
const streamBytes = Buffer.from(stream, "utf-8");
|
|
188
|
+
|
|
189
|
+
const objects: string[] = [];
|
|
190
|
+
const offsets: number[] = [];
|
|
191
|
+
let body = "";
|
|
192
|
+
|
|
193
|
+
offsets.push(body.length);
|
|
194
|
+
objects.push("1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n");
|
|
195
|
+
body += objects[0];
|
|
196
|
+
|
|
197
|
+
offsets.push(body.length);
|
|
198
|
+
objects.push("2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n");
|
|
199
|
+
body += objects[1];
|
|
200
|
+
|
|
201
|
+
offsets.push(body.length);
|
|
202
|
+
objects.push(
|
|
203
|
+
"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>\nendobj\n"
|
|
204
|
+
);
|
|
205
|
+
body += objects[2];
|
|
206
|
+
|
|
207
|
+
offsets.push(body.length);
|
|
208
|
+
objects.push(
|
|
209
|
+
`4 0 obj\n<< /Length ${streamBytes.length} >>\nstream\n${stream}\nendstream\nendobj\n`
|
|
210
|
+
);
|
|
211
|
+
body += objects[3];
|
|
212
|
+
|
|
213
|
+
offsets.push(body.length);
|
|
214
|
+
objects.push(
|
|
215
|
+
"5 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n"
|
|
216
|
+
);
|
|
217
|
+
body += objects[4];
|
|
218
|
+
|
|
219
|
+
const header = "%PDF-1.4\n";
|
|
220
|
+
const xrefOffset = header.length + body.length;
|
|
221
|
+
|
|
222
|
+
let xref = `xref\n0 ${offsets.length + 1}\n0000000000 65535 f \n`;
|
|
223
|
+
for (const offset of offsets) {
|
|
224
|
+
xref += `${String(header.length + offset).padStart(10, "0")} 00000 n \n`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const trailer = `trailer\n<< /Size ${offsets.length + 1} /Root 1 0 R >>\nstartxref\n${xrefOffset}\n%%EOF`;
|
|
228
|
+
|
|
229
|
+
return Buffer.from(header + body + xref + trailer, "utf-8");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function escapeXml(text: string): string {
|
|
233
|
+
return text
|
|
234
|
+
.replace(/&/g, "&")
|
|
235
|
+
.replace(/</g, "<")
|
|
236
|
+
.replace(/>/g, ">")
|
|
237
|
+
.replace(/"/g, """);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function escapePdf(text: string): string {
|
|
241
|
+
return text
|
|
242
|
+
.replace(/\\/g, "\\\\")
|
|
243
|
+
.replace(/\(/g, "\\(")
|
|
244
|
+
.replace(/\)/g, "\\)");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// --- Document definitions ---
|
|
248
|
+
|
|
249
|
+
const DOCUMENTS: DocumentDef[] = [
|
|
250
|
+
// Project 1 — Investment Portfolio
|
|
251
|
+
{
|
|
252
|
+
originalName: "portfolio-holdings.xlsx",
|
|
253
|
+
mimeType:
|
|
254
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
255
|
+
projectIndex: 0,
|
|
256
|
+
taskIndex: 0,
|
|
257
|
+
content: () =>
|
|
258
|
+
Promise.resolve(
|
|
259
|
+
createXlsxSync(
|
|
260
|
+
`Ticker,Shares,Avg Cost,Current Price,Market Value,Sector
|
|
261
|
+
NVDA,45,285.50,890.25,40061.25,Technology
|
|
262
|
+
AAPL,120,142.30,178.50,21420.00,Technology
|
|
263
|
+
MSFT,65,310.00,415.80,27027.00,Technology
|
|
264
|
+
GOOGL,80,125.40,172.30,13784.00,Technology
|
|
265
|
+
AMZN,55,128.50,185.60,10208.00,Consumer
|
|
266
|
+
JNJ,90,158.20,155.80,14022.00,Healthcare
|
|
267
|
+
UNH,25,485.00,528.40,13210.00,Healthcare
|
|
268
|
+
JPM,70,142.80,198.50,13895.00,Finance
|
|
269
|
+
V,50,235.60,282.40,14120.00,Finance
|
|
270
|
+
XOM,85,98.40,108.20,9197.00,Energy
|
|
271
|
+
PG,60,148.50,165.20,9912.00,Consumer
|
|
272
|
+
LLY,15,580.00,782.50,11737.50,Healthcare
|
|
273
|
+
HD,35,325.00,348.90,12211.50,Consumer
|
|
274
|
+
MA,40,368.50,462.80,18512.00,Finance
|
|
275
|
+
CVX,55,152.30,158.40,8712.00,Energy`
|
|
276
|
+
)
|
|
277
|
+
),
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
originalName: "semiconductor-research.pdf",
|
|
281
|
+
mimeType: "application/pdf",
|
|
282
|
+
projectIndex: 0,
|
|
283
|
+
taskIndex: 1,
|
|
284
|
+
content: () =>
|
|
285
|
+
Promise.resolve(
|
|
286
|
+
createPdfSync(
|
|
287
|
+
`Semiconductor Industry ETF Analysis
|
|
288
|
+
|
|
289
|
+
Market Overview
|
|
290
|
+
The semiconductor sector has seen exceptional growth driven by AI
|
|
291
|
+
infrastructure demand, automotive chip adoption, and cloud computing.
|
|
292
|
+
Total addressable market projected at $1.2T by 2030.
|
|
293
|
+
|
|
294
|
+
SOXX - iShares Semiconductor ETF
|
|
295
|
+
Expense Ratio: 0.35%, Holdings: 30, 5-Year Return: +142%
|
|
296
|
+
Top 5: NVDA 9.8%, AVGO 8.2%, AMD 7.1%, QCOM 5.5%, TXN 5.2%
|
|
297
|
+
|
|
298
|
+
SMH - VanEck Semiconductor ETF
|
|
299
|
+
Expense Ratio: 0.35%, Holdings: 25, 5-Year Return: +158%
|
|
300
|
+
Top 5: NVDA 14.2%, TSM 12.8%, AVGO 6.5%, AMD 5.1%, ASML 4.8%
|
|
301
|
+
|
|
302
|
+
PSI - Invesco Dynamic Semiconductors ETF
|
|
303
|
+
Expense Ratio: 0.56%, Holdings: 30, 5-Year Return: +98%
|
|
304
|
+
|
|
305
|
+
Recommendation: SMH for concentrated AI exposure, SOXX for diversification.`
|
|
306
|
+
)
|
|
307
|
+
),
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
// Project 2 — Landing Page
|
|
311
|
+
{
|
|
312
|
+
originalName: "competitor-audit.docx",
|
|
313
|
+
mimeType:
|
|
314
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
315
|
+
projectIndex: 1,
|
|
316
|
+
taskIndex: 5,
|
|
317
|
+
content: () =>
|
|
318
|
+
createDocx(
|
|
319
|
+
`Competitor Landing Page Audit
|
|
320
|
+
|
|
321
|
+
1. Notion (notion.so)
|
|
322
|
+
Hero: "Your wiki, docs, & projects. Together."
|
|
323
|
+
Strengths: Clean design, interactive demo, strong social proof (10M+ users)
|
|
324
|
+
Weaknesses: Hero is text-heavy, no personalization
|
|
325
|
+
CTA: "Get Notion free" - low commitment
|
|
326
|
+
|
|
327
|
+
2. Linear (linear.app)
|
|
328
|
+
Hero: "Linear is a purpose-built tool for planning and building products"
|
|
329
|
+
Strengths: Dark theme feels premium, keyboard shortcut demo, speed-focused
|
|
330
|
+
Weaknesses: Appeals mainly to developers, narrow audience
|
|
331
|
+
CTA: "Start building" - action-oriented
|
|
332
|
+
|
|
333
|
+
3. Vercel (vercel.com)
|
|
334
|
+
Hero: "Develop. Preview. Ship." - three-word framework
|
|
335
|
+
Strengths: Live deployment demo, framework logos, dark/light toggle
|
|
336
|
+
Weaknesses: Very technical, assumes Next.js knowledge
|
|
337
|
+
CTA: "Start Deploying" - verb-first
|
|
338
|
+
|
|
339
|
+
4. Stripe (stripe.com)
|
|
340
|
+
Hero: "Financial infrastructure for the internet"
|
|
341
|
+
Strengths: Animated code snippets, multiple audience entry points, trust badges
|
|
342
|
+
Weaknesses: Dense page, high cognitive load
|
|
343
|
+
CTA: "Start now" with "Contact sales" secondary
|
|
344
|
+
|
|
345
|
+
Key Patterns:
|
|
346
|
+
- All use social proof above the fold
|
|
347
|
+
- 2 of 4 feature interactive demos
|
|
348
|
+
- Average CTA count: 3 per page
|
|
349
|
+
|
|
350
|
+
Opportunity: Dynamic hero based on referral source. None of the 4 do this.`
|
|
351
|
+
),
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
originalName: "design-brief.pdf",
|
|
355
|
+
mimeType: "application/pdf",
|
|
356
|
+
projectIndex: 1,
|
|
357
|
+
taskIndex: 6,
|
|
358
|
+
content: () =>
|
|
359
|
+
Promise.resolve(
|
|
360
|
+
createPdfSync(
|
|
361
|
+
`Landing Page Design Brief
|
|
362
|
+
|
|
363
|
+
Brand Voice
|
|
364
|
+
Tone: Confident, not arrogant. Technical, but accessible.
|
|
365
|
+
Personality: The smart colleague who explains things clearly
|
|
366
|
+
Avoid: Jargon-heavy copy, exclamation marks, hyperbole
|
|
367
|
+
|
|
368
|
+
Target Audience - Primary: Engineering Managers
|
|
369
|
+
Age 30-45, managing 5-20 person teams
|
|
370
|
+
Pain points: sprint planning overhead, context switching, tool fatigue
|
|
371
|
+
|
|
372
|
+
Target Audience - Secondary: Individual Developers
|
|
373
|
+
Age 25-35, working in fast-paced startups
|
|
374
|
+
Pain points: slow tooling, too many tabs, broken workflows
|
|
375
|
+
|
|
376
|
+
Messaging Pillars
|
|
377
|
+
1. Speed - "Build faster"
|
|
378
|
+
2. Intelligence - AI that understands your codebase
|
|
379
|
+
3. Simplicity - One tool, not ten
|
|
380
|
+
|
|
381
|
+
Visual Direction
|
|
382
|
+
- OKLCH color system, primary hue 250 (blue-indigo)
|
|
383
|
+
- Monospace code snippets for technical credibility
|
|
384
|
+
- Subtle animations on scroll
|
|
385
|
+
|
|
386
|
+
Conversion Goals
|
|
387
|
+
Primary: Sign up for free trial
|
|
388
|
+
Secondary: Book a demo (enterprise)
|
|
389
|
+
Tertiary: Newsletter subscription`
|
|
390
|
+
)
|
|
391
|
+
),
|
|
392
|
+
},
|
|
393
|
+
|
|
394
|
+
// Project 3 — Lead Generation
|
|
395
|
+
{
|
|
396
|
+
originalName: "prospect-list.xlsx",
|
|
397
|
+
mimeType:
|
|
398
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
399
|
+
projectIndex: 2,
|
|
400
|
+
taskIndex: 10,
|
|
401
|
+
content: () =>
|
|
402
|
+
Promise.resolve(
|
|
403
|
+
createXlsxSync(
|
|
404
|
+
`Name,Title,Company,Industry,Size,LinkedIn URL,Email,Status
|
|
405
|
+
Sarah Chen,VP Engineering,Acme Corp,SaaS,500-1000,linkedin.com/in/sarachen,s.chen@acmecorp.com,Qualified
|
|
406
|
+
Marcus Johnson,Director of Engineering,Acme Corp,SaaS,500-1000,linkedin.com/in/marcusjohnson,m.johnson@acmecorp.com,Qualified
|
|
407
|
+
Rachel Kim,VP Product,Acme Corp,SaaS,500-1000,linkedin.com/in/rachelkim,r.kim@acmecorp.com,Qualified
|
|
408
|
+
David Park,CTO,TechStart Inc,DevTools,50-200,linkedin.com/in/davidpark,d.park@techstart.io,Qualified
|
|
409
|
+
Lisa Wang,VP Engineering,TechStart Inc,DevTools,50-200,linkedin.com/in/lisawang,l.wang@techstart.io,Qualified
|
|
410
|
+
James Miller,VP Product,CloudBase,Infrastructure,200-500,linkedin.com/in/jamesmiller,j.miller@cloudbase.dev,Contacted
|
|
411
|
+
Maria Garcia,Director of Engineering,DataFlow AI,AI/ML,100-200,linkedin.com/in/mariagarcia,m.garcia@dataflow.ai,Qualified
|
|
412
|
+
Tom Anderson,VP Engineering,ScaleUp HQ,FinTech,500-1000,linkedin.com/in/tomanderson,t.anderson@scaleuphq.com,Qualified
|
|
413
|
+
Nina Patel,CTO,QuickShip,Logistics,200-500,linkedin.com/in/ninapatel,n.patel@quickship.co,Researching
|
|
414
|
+
Alex Turner,Director of Engineering,BrightPath,EdTech,100-200,linkedin.com/in/alexturner,a.turner@brightpath.edu,Qualified
|
|
415
|
+
Sophie Reed,VP Product,GreenGrid,CleanTech,50-200,linkedin.com/in/sophiereed,s.reed@greengrid.io,Researching
|
|
416
|
+
Chris Wong,VP Engineering,NexaPay,FinTech,200-500,linkedin.com/in/chriswong,c.wong@nexapay.com,Qualified`
|
|
417
|
+
)
|
|
418
|
+
),
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
originalName: "outreach-templates.docx",
|
|
422
|
+
mimeType:
|
|
423
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
424
|
+
projectIndex: 2,
|
|
425
|
+
taskIndex: 12,
|
|
426
|
+
content: () =>
|
|
427
|
+
createDocx(
|
|
428
|
+
`Personalized Outreach Email Templates
|
|
429
|
+
|
|
430
|
+
Template 1: Pain Point Hook
|
|
431
|
+
Subject: {Company}'s engineering velocity - quick question
|
|
432
|
+
|
|
433
|
+
Hi {FirstName},
|
|
434
|
+
|
|
435
|
+
I noticed {Company} recently {recent_event}. Congrats!
|
|
436
|
+
|
|
437
|
+
When teams scale past {team_size} engineers, we often see sprint planning eat up 20%+ of leadership time. Is that something you're running into?
|
|
438
|
+
|
|
439
|
+
We built something that cuts that overhead in half using AI-powered task routing. Would love 15 minutes to show you how {similar_company} uses it.
|
|
440
|
+
|
|
441
|
+
Best, [Sender]
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
Template 2: Social Proof
|
|
445
|
+
Subject: How {similar_company} ships 2x faster
|
|
446
|
+
|
|
447
|
+
Hi {FirstName},
|
|
448
|
+
|
|
449
|
+
{similar_company}'s VP of Eng shared something interesting - their team went from 2-week sprints to continuous delivery. Deployment frequency up 180%.
|
|
450
|
+
|
|
451
|
+
I think {Company} could see similar results given your {tech_stack} setup. Worth a quick chat?
|
|
452
|
+
|
|
453
|
+
Cheers, [Sender]
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
Template 3: Breakup / Last Touch
|
|
457
|
+
Subject: Closing the loop
|
|
458
|
+
|
|
459
|
+
Hi {FirstName},
|
|
460
|
+
|
|
461
|
+
I've reached out a couple times and totally get it if the timing isn't right.
|
|
462
|
+
|
|
463
|
+
If engineering velocity is ever a priority for {Company}, here's what we do: [one-liner + link].
|
|
464
|
+
|
|
465
|
+
No hard feelings. The door's always open.
|
|
466
|
+
|
|
467
|
+
All the best, [Sender]`
|
|
468
|
+
),
|
|
469
|
+
},
|
|
470
|
+
|
|
471
|
+
// Project 4 — Business Trip
|
|
472
|
+
{
|
|
473
|
+
originalName: "trip-itinerary.docx",
|
|
474
|
+
mimeType:
|
|
475
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
476
|
+
projectIndex: 3,
|
|
477
|
+
taskIndex: 17,
|
|
478
|
+
content: () =>
|
|
479
|
+
createDocx(
|
|
480
|
+
`NYC Business Trip Itinerary - March 15-18, 2025
|
|
481
|
+
|
|
482
|
+
Day 1 - Saturday, March 15
|
|
483
|
+
7:00 AM Depart SFO - United UA 456, Gate B12 (TSA Pre)
|
|
484
|
+
3:30 PM Arrive JFK Terminal 7 - Uber to hotel (~45 min)
|
|
485
|
+
4:30 PM Hotel check-in - The Manhattan Club, 200 W 56th St
|
|
486
|
+
6:00 PM Conference keynote - Javits Center, Hall 1A
|
|
487
|
+
8:00 PM Team dinner - Carbone, 181 Thompson St
|
|
488
|
+
|
|
489
|
+
Day 2 - Sunday, March 16
|
|
490
|
+
8:30 AM Breakfast meeting - The Smith (J. Rivera, Acme Corp)
|
|
491
|
+
10:00 AM Partner meeting #1 - Acme Corp HQ, 350 5th Ave
|
|
492
|
+
12:00 PM Lunch - Le Bernardin, 155 W 51st St
|
|
493
|
+
2:00 PM Client meeting - DataFlow AI, 85 Broad St
|
|
494
|
+
4:30 PM Partner meeting #2 - ScaleUp HQ, 28 W 23rd St
|
|
495
|
+
7:00 PM Networking event - Rooftop at The Standard
|
|
496
|
+
|
|
497
|
+
Day 3 - Monday, March 17
|
|
498
|
+
9:00 AM Workshop - Javits Center, Room 204
|
|
499
|
+
12:00 PM Working lunch - Hotel lobby cafe
|
|
500
|
+
2:00 PM Team standup (Zoom)
|
|
501
|
+
3:00 PM Hotel checkout
|
|
502
|
+
6:00 PM Depart JFK - United UA 891, Terminal 7
|
|
503
|
+
9:15 PM Arrive SFO
|
|
504
|
+
|
|
505
|
+
Travel Budget:
|
|
506
|
+
Flights: $660 | Hotel: $867 | Meals: $225
|
|
507
|
+
Ground transport: $200 | Misc: $100
|
|
508
|
+
Total: $2,052 (Pre-approved #EXP-2025-0342)`
|
|
509
|
+
),
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
originalName: "expense-report.xlsx",
|
|
513
|
+
mimeType:
|
|
514
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
515
|
+
projectIndex: 3,
|
|
516
|
+
taskIndex: 19,
|
|
517
|
+
content: () =>
|
|
518
|
+
Promise.resolve(
|
|
519
|
+
createXlsxSync(
|
|
520
|
+
`Date,Category,Vendor,Amount,Description,Receipt
|
|
521
|
+
2025-03-15,Airfare,United Airlines,342.00,SFO to JFK Economy Plus UA 456,receipt-001.pdf
|
|
522
|
+
2025-03-15,Ground Transport,Uber,62.50,JFK to Manhattan Club hotel,receipt-002.pdf
|
|
523
|
+
2025-03-15,Meals,Carbone Restaurant,89.00,Team dinner (4 people split),receipt-003.pdf
|
|
524
|
+
2025-03-16,Meals,The Smith NYC,28.50,Breakfast meeting with J. Rivera,receipt-004.pdf
|
|
525
|
+
2025-03-16,Ground Transport,Uber,18.75,Hotel to Acme Corp,receipt-005.pdf
|
|
526
|
+
2025-03-16,Meals,Le Bernardin,65.00,Working lunch,receipt-006.pdf
|
|
527
|
+
2025-03-16,Ground Transport,Uber,32.40,Acme to DataFlow AI,receipt-007.pdf
|
|
528
|
+
2025-03-16,Ground Transport,Lyft,24.80,DataFlow to ScaleUp HQ,receipt-008.pdf
|
|
529
|
+
2025-03-16,Meals,Rooftop at The Standard,42.00,Networking event,receipt-009.pdf
|
|
530
|
+
2025-03-17,Meals,Hotel Lobby Cafe,18.50,Working lunch,receipt-010.pdf
|
|
531
|
+
2025-03-17,Ground Transport,Uber,58.20,Manhattan Club to JFK,receipt-011.pdf
|
|
532
|
+
2025-03-17,Conference,TechConf 2025,299.00,Conference registration,receipt-012.pdf
|
|
533
|
+
2025-03-15,Hotel,The Manhattan Club,289.00,Night 1 Standard King,receipt-013.pdf
|
|
534
|
+
2025-03-16,Hotel,The Manhattan Club,289.00,Night 2 Standard King,receipt-014.pdf
|
|
535
|
+
2025-03-17,Hotel,The Manhattan Club,289.00,Night 3 Standard King,receipt-015.pdf
|
|
536
|
+
2025-03-15,Meals,Starbucks JFK,8.40,Coffee at terminal,receipt-016.pdf
|
|
537
|
+
2025-03-16,Miscellaneous,CVS Pharmacy,12.80,Phone charger,receipt-017.pdf
|
|
538
|
+
2025-03-17,Miscellaneous,Hotel Concierge,15.00,Luggage storage tip,receipt-018.pdf`
|
|
539
|
+
)
|
|
540
|
+
),
|
|
541
|
+
},
|
|
542
|
+
|
|
543
|
+
// Project 5 — Tax Filing
|
|
544
|
+
{
|
|
545
|
+
originalName: "tax-documents-checklist.pdf",
|
|
546
|
+
mimeType: "application/pdf",
|
|
547
|
+
projectIndex: 4,
|
|
548
|
+
taskIndex: 20,
|
|
549
|
+
content: () =>
|
|
550
|
+
Promise.resolve(
|
|
551
|
+
createPdfSync(
|
|
552
|
+
`2025 Tax Documents Checklist
|
|
553
|
+
|
|
554
|
+
INCOME DOCUMENTS
|
|
555
|
+
[X] W-2 - TechCorp Inc (primary employer)
|
|
556
|
+
Gross wages: $185,000 | Federal withholding: $37,200
|
|
557
|
+
State withholding: $12,580 | Received: Jan 28
|
|
558
|
+
|
|
559
|
+
[X] 1099-INT - Chase Bank (savings interest)
|
|
560
|
+
Interest income: $1,240 | Received: Feb 2
|
|
561
|
+
|
|
562
|
+
[X] 1099-DIV - Fidelity Investments
|
|
563
|
+
Ordinary dividends: $3,850 | Qualified: $2,910
|
|
564
|
+
|
|
565
|
+
[X] 1099-B - Charles Schwab (stock sales)
|
|
566
|
+
Proceeds: $42,800 | Cost basis: $38,200 | Net gain: $4,600
|
|
567
|
+
|
|
568
|
+
[ ] 1099-NEC - Freelance Client (PENDING)
|
|
569
|
+
Expected: ~$8,500
|
|
570
|
+
|
|
571
|
+
DEDUCTION DOCUMENTS
|
|
572
|
+
[X] 1098 - Wells Fargo (mortgage interest)
|
|
573
|
+
Interest paid: $18,400 | Property tax: $6,200
|
|
574
|
+
|
|
575
|
+
[X] Charitable donations - receipts compiled
|
|
576
|
+
Cash: $2,400 | Goodwill (FMV): $800
|
|
577
|
+
|
|
578
|
+
[ ] Home office measurements - pending calculation
|
|
579
|
+
|
|
580
|
+
[X] State estimated tax payments
|
|
581
|
+
Q1-Q4: $3,200 each | Total: $12,800
|
|
582
|
+
|
|
583
|
+
STATUS: 7 of 8 documents collected (87.5%)`
|
|
584
|
+
)
|
|
585
|
+
),
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
originalName: "deductible-expenses.xlsx",
|
|
589
|
+
mimeType:
|
|
590
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
591
|
+
projectIndex: 4,
|
|
592
|
+
taskIndex: 21,
|
|
593
|
+
content: () =>
|
|
594
|
+
Promise.resolve(
|
|
595
|
+
createXlsxSync(
|
|
596
|
+
`Date,Category,Description,Amount,Deductible,Notes
|
|
597
|
+
2025-01-05,Home Office,Internet service (pro-rata),45.00,Yes,8.3% of total
|
|
598
|
+
2025-01-05,Home Office,Electricity (pro-rata),32.00,Yes,8.3% of total
|
|
599
|
+
2025-01-12,Professional Dev,Udemy - Advanced TypeScript,29.99,Yes,Job-related
|
|
600
|
+
2025-01-15,Software,GitHub Pro subscription,7.00,Yes,Monthly
|
|
601
|
+
2025-01-15,Software,Figma Professional,15.00,Yes,Monthly
|
|
602
|
+
2025-01-20,Charitable,SF Food Bank donation,200.00,Yes,Receipt #4521
|
|
603
|
+
2025-02-01,Home Office,Ergonomic desk chair,485.00,Partial,May depreciate
|
|
604
|
+
2025-02-05,Home Office,Internet service (pro-rata),45.00,Yes,Monthly
|
|
605
|
+
2025-02-05,Home Office,Electricity (pro-rata),28.00,Yes,Lower winter bill
|
|
606
|
+
2025-02-10,Professional Dev,AWS SA exam fee,300.00,Yes,Certification
|
|
607
|
+
2025-02-14,Charitable,American Red Cross,150.00,Yes,Confirmation email
|
|
608
|
+
2025-02-15,Software,GitHub Pro subscription,7.00,Yes,Monthly
|
|
609
|
+
2025-02-15,Software,Figma Professional,15.00,Yes,Monthly
|
|
610
|
+
2025-03-01,Health,HSA contribution,500.00,Yes,Monthly
|
|
611
|
+
2025-03-05,Home Office,Internet service (pro-rata),45.00,Yes,Monthly
|
|
612
|
+
2025-03-05,Home Office,Electricity (pro-rata),35.00,Yes,Monthly
|
|
613
|
+
2025-03-10,Professional Dev,O'Reilly Safari,49.00,Yes,Annual
|
|
614
|
+
2025-03-12,Charitable,Local animal shelter,100.00,Yes,Receipt #782
|
|
615
|
+
2025-03-15,Software,GitHub Pro subscription,7.00,Yes,Monthly
|
|
616
|
+
2025-03-15,Software,Figma Professional,15.00,Yes,Monthly
|
|
617
|
+
2025-03-15,Equipment,External monitor (4K),650.00,Partial,Section 179
|
|
618
|
+
2025-03-20,Professional Dev,React Conference,350.00,Yes,Job-related
|
|
619
|
+
2025-04-01,Health,HSA contribution,500.00,Yes,Monthly
|
|
620
|
+
2025-04-05,Home Office,Internet (pro-rata),45.00,Yes,Monthly
|
|
621
|
+
2025-04-05,Home Office,Electricity (pro-rata),38.00,Yes,Spring cycle`
|
|
622
|
+
)
|
|
623
|
+
),
|
|
624
|
+
},
|
|
625
|
+
|
|
626
|
+
// Extra office docs for variety
|
|
627
|
+
|
|
628
|
+
// Campaign pitch deck for lead gen
|
|
629
|
+
{
|
|
630
|
+
originalName: "campaign-pitch-deck.pptx",
|
|
631
|
+
mimeType:
|
|
632
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
633
|
+
projectIndex: 2,
|
|
634
|
+
taskIndex: 13,
|
|
635
|
+
content: () =>
|
|
636
|
+
createPptx([
|
|
637
|
+
"Lead Generation Campaign - Q2 2025",
|
|
638
|
+
"Target Market: VP/Director-level at B2B SaaS companies (50-1000 employees)",
|
|
639
|
+
"Campaign Strategy: 3-touch email sequence over 14 days, segmented by company size and role",
|
|
640
|
+
"Prospect Pipeline: 15 qualified leads across 8 companies",
|
|
641
|
+
"Email Templates: Pain Point Hook (cold), Social Proof (warm), Breakup (last touch)",
|
|
642
|
+
"Success Metrics: 40% open rate target, 8% reply rate, 3 meetings booked per week",
|
|
643
|
+
"Timeline: Week 1 first touch, Week 2 follow-ups, Week 3 breakup + new prospects",
|
|
644
|
+
"Budget: $0 organic outreach + $500/mo email tooling",
|
|
645
|
+
]),
|
|
646
|
+
},
|
|
647
|
+
|
|
648
|
+
// Quarterly performance summary for portfolio project
|
|
649
|
+
{
|
|
650
|
+
originalName: "q1-performance-summary.pptx",
|
|
651
|
+
mimeType:
|
|
652
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
653
|
+
projectIndex: 0,
|
|
654
|
+
taskIndex: 3,
|
|
655
|
+
content: () =>
|
|
656
|
+
createPptx([
|
|
657
|
+
"Q1 2025 Portfolio Performance Summary",
|
|
658
|
+
"Portfolio Value: $238,419 (+12.4% YTD vs S&P 500 +8.2%)",
|
|
659
|
+
"Top Performers: NVDA +34%, LLY +22%, MA +18%",
|
|
660
|
+
"Underperformers: JNJ -1.5%, CVX +3.8%, XOM +2.1%",
|
|
661
|
+
"Sector Allocation: Tech 42%, Healthcare 18%, Finance 15%, Consumer 12%, Energy 8%, Cash 5%",
|
|
662
|
+
"Risk Assessment: Tech concentration at 42% exceeds 35% target",
|
|
663
|
+
"Dividend Income: $1,842 Q1 ($7,368 annualized, 3.1% yield)",
|
|
664
|
+
"Action Items: Trim NVDA by 20 shares, Add UNH +10, Initiate SCHD position",
|
|
665
|
+
]),
|
|
666
|
+
},
|
|
667
|
+
];
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Write document files to disk and return seed records.
|
|
671
|
+
* Async because DOCX/PPTX generation uses JSZip's async API.
|
|
672
|
+
*/
|
|
673
|
+
export async function createDocuments(
|
|
674
|
+
projectIds: string[],
|
|
675
|
+
taskIds: string[]
|
|
676
|
+
): Promise<DocumentSeed[]> {
|
|
677
|
+
mkdirSync(uploadsDir, { recursive: true });
|
|
678
|
+
|
|
679
|
+
const results: DocumentSeed[] = [];
|
|
680
|
+
|
|
681
|
+
for (const def of DOCUMENTS) {
|
|
682
|
+
const id = crypto.randomUUID();
|
|
683
|
+
const ext = def.originalName.split(".").pop()!;
|
|
684
|
+
const filename = `${id}.${ext}`;
|
|
685
|
+
const storagePath = join(uploadsDir, filename);
|
|
686
|
+
|
|
687
|
+
let buf: Buffer;
|
|
688
|
+
if (typeof def.content === "function") {
|
|
689
|
+
buf = await def.content();
|
|
690
|
+
} else {
|
|
691
|
+
buf = Buffer.from(def.content, "utf-8");
|
|
692
|
+
}
|
|
693
|
+
writeFileSync(storagePath, buf);
|
|
694
|
+
|
|
695
|
+
const taskId = taskIds[def.taskIndex];
|
|
696
|
+
const projectId = projectIds[def.projectIndex];
|
|
697
|
+
|
|
698
|
+
results.push({
|
|
699
|
+
id,
|
|
700
|
+
taskId,
|
|
701
|
+
projectId,
|
|
702
|
+
filename,
|
|
703
|
+
originalName: def.originalName,
|
|
704
|
+
mimeType: def.mimeType,
|
|
705
|
+
size: buf.length,
|
|
706
|
+
storagePath,
|
|
707
|
+
direction: "input" as const,
|
|
708
|
+
status: "uploaded" as const,
|
|
709
|
+
createdAt: new Date(),
|
|
710
|
+
updatedAt: new Date(),
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return results;
|
|
715
|
+
}
|