Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .opencode/openwork.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": 1,
"workspace": {
"name": "zerofinance",
"createdAt": 1768940142851,
"preset": "starter"
},
"authorizedRoots": [
"/Users/benjaminshafii/git/zerofinance"
]
}
2 changes: 1 addition & 1 deletion .opencode/plugin/test-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const testCases = [
`Some text before
<update-skills>
- learned: Vercel logs require --scope prologe flag
- skill: debug-prod-data
- skill: debug-prod-issues
- improvement: Add note about scope flag
</update-skills>
Some text after`,
Expand Down
2 changes: 2 additions & 0 deletions .opencode/skill/testability/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ pnpm test:watch # Watch mode
pnpm test -- --run --grep "fee" # Filter by name
```

> Repo note: `@zero-finance/web` Vitest discovers tests under `packages/web/src/test/**/*.test.ts`. Put new tests there (or update Vitest config) so they get picked up.

---

## Layer 2: API/Integration Tests
Expand Down
27 changes: 23 additions & 4 deletions .opencode/skill/vercel-dns/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,12 @@ vercel dns add <domain> <subdomain> CNAME <target>
vercel dns add <domain> <subdomain> TXT '<value>'

# Add MX record with priority
vercel dns add <domain> <subdomain> MX '<priority> <mail-server>'
# NOTE: MX priority is a separate argument (not embedded in the value)
vercel dns add <domain> <subdomain> MX <mail-server> <priority>

# Add record at apex (root domain) - use empty string or @
vercel dns add <domain> '' TXT '<value>'
vercel dns add <domain> @ MX '10 mail.example.com'
vercel dns add <domain> @ MX mail.example.com 10
```

### Remove DNS Records
Expand All @@ -99,7 +100,12 @@ vercel dns rm <record-id> --yes

```bash
# Add MX record for receiving email
vercel dns add example.com '' MX '10 inbound-smtp.us-east-1.amazonaws.com'
# WARNING: Setting MX at the apex (root) will route *all* inbound mail for the domain.
# If the domain already uses Google Workspace / Fastmail / etc, prefer a dedicated subdomain.
vercel dns add example.com '' MX inbound-smtp.us-east-1.amazonaws.com 10

# Safer: use a subdomain for inbound routing
vercel dns add example.com inbound MX inbound-smtp.us-east-1.amazonaws.com 10

# Add SPF record
vercel dns add example.com '' TXT 'v=spf1 include:amazonses.com ~all'
Expand Down Expand Up @@ -148,9 +154,22 @@ vercel switch <correct-team-slug>
### Record Not Propagating

- DNS propagation can take up to 48 hours (usually 5-30 minutes)
- Check propagation: `dig <record-name> <record-type>` or use dnschecker.org
- Check propagation: `dig +short <record-name> <record-type>` or use dnschecker.org
- Verify record was added: `vercel dns ls <domain>`

## Token Saving Tips

### Fast Verification With `dig`

When a third-party UI says "Looking for DNS records", verify what the public internet sees:

```bash
dig +short TXT resend._domainkey.example.com
dig +short TXT send.example.com
dig +short MX send.example.com
dig +short TXT example.com
```

## Tips

1. Always check which team you're in before making changes
Expand Down
47 changes: 47 additions & 0 deletions .opencode/skill/workspace-guide/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
name: workspace-guide
description: Workspace guide to introduce OpenWork and onboard new users.
---

# Welcome to OpenWork

Hi, I\'m Ben and this is OpenWork. It\'s an open-source alternative to Claude\'s cowork. It helps you work on your files with AI and automate the mundane tasks so you don\'t have to.

Before we start, use the question tool to ask:
"Are you more technical or non-technical? I\'ll tailor the explanation."

## If the person is non-technical
OpenWork feels like a chat app, but it can safely work with the files you allow. Put files in this workspace and I can summarize them, create new ones, or help organize them.

Try:
- "Summarize the files in this workspace."
- "Create a checklist for my week."
- "Draft a short summary from this document."

## Skills and plugins (simple)
Skills add new capabilities. Plugins add advanced features like scheduling or browser automation. We can add them later when you\'re ready.

## If the person is technical
OpenWork is a GUI for OpenCode. Everything that works in OpenCode works here.

Most reliable setup today:
1) Install OpenCode from opencode.ai
2) Configure providers there (models and API keys)
3) Come back to OpenWork and start a session

Skills:
- Install from the Skills tab, or add them to this workspace.
- Docs: https://opencode.ai/docs/skills

Plugins:
- Configure in opencode.json or use the Plugins tab.
- Docs: https://opencode.ai/docs/plugins/

MCP servers:
- Add external tools via opencode.json.
- Docs: https://opencode.ai/docs/mcp-servers/

Config reference:
- Docs: https://opencode.ai/docs/config/

End with two friendly next actions to try in OpenWork.
7 changes: 7 additions & 0 deletions .openwork/templates/interact-with-files/template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
id: 'interact-with-files'
title: 'Learn to interact with files'
description: 'Safe, practical file workflows'
createdAt: 1768940142850
---
Show me how to interact with files in this workspace. Include safe examples for reading, summarizing, and editing.
7 changes: 7 additions & 0 deletions .openwork/templates/learn-plugins/template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
id: 'learn-plugins'
title: 'Learn about plugins'
description: 'What plugins are and how to install them'
createdAt: 1768940142850
---
Explain what plugins are and how to install them in this workspace.
7 changes: 7 additions & 0 deletions .openwork/templates/learn-skills/template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
id: 'learn-skills'
title: 'Learn about skills'
description: 'How skills work and how to create your own'
createdAt: 1768940142850
---
Explain what skills are, how to use them, and how to create a new skill for this workspace.
18 changes: 9 additions & 9 deletions opencode.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@
"$schema": "https://opencode.ai/config.json",
"mcp": {
"exa": {
"headers": {},
"type": "remote",
"url": "https://mcp.exa.ai/mcp?tools=web_search_exa,get_code_context_exa,crawling_exa",
"headers": {}
"url": "https://mcp.exa.ai/mcp?tools=web_search_exa,get_code_context_exa,crawling_exa"
},
"notion": {
"enabled": true,
"oauth": {},
"type": "remote",
"url": "https://mcp.notion.com/mcp",
"enabled": true
"url": "https://mcp.notion.com/mcp"
},
"zero-finance": {
"type": "remote",
"url": "https://www.0.finance/api/mcp",
"headers": {
"Authorization": "Bearer "
"Authorization": "Bearer {env:ZERO_FINANCE_MCP_TOKEN}"
},
"enabled": true
"type": "remote",
"url": "https://www.0.finance/api/mcp"
}
},
"plugin": ["@different-ai/opencode-browser"]
"plugin": ["opencode-scheduler"]
}
1 change: 1 addition & 0 deletions packages/web/drizzle/0131_insurance_coverage_amount.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE "workspaces" ADD COLUMN "insurance_coverage_usd" integer DEFAULT 100000 NOT NULL;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useRouter, useSearchParams } from 'next/navigation';
import Link from 'next/link';
import { api } from '@/trpc/react';
import {
Expand All @@ -15,8 +15,19 @@ import {
import GeneratedComponent from '@/app/(landing)/welcome-gradient';
import { toast } from 'sonner';

import {
formatCoverageUsd,
parseCoverageAmountParam,
} from '@/lib/insurance/coverage-amount';

export default function InsuranceActivatePage() {
const router = useRouter();
const searchParams = useSearchParams();

const coverageUsd = parseCoverageAmountParam(
searchParams.get('coverage') ?? searchParams.get('amount') ?? undefined,
);
const coverageDisplay = formatCoverageUsd(coverageUsd, { style: 'compact' });
const [step, setStep] = useState<'initial' | 'loading' | 'success'>(
'initial',
);
Expand Down Expand Up @@ -61,7 +72,7 @@ export default function InsuranceActivatePage() {
setCurrentLoadingStep(0);

try {
await activateInsurance.mutateAsync(undefined);
await activateInsurance.mutateAsync({ coverageUsd });

// Invalidate user profile to refresh insurance status
await utils.user.getProfile.invalidate();
Expand Down Expand Up @@ -122,17 +133,17 @@ export default function InsuranceActivatePage() {
<span className="text-[13px] sm:text-[14px] text-[#101010]/80 leading-[1.5]">
I have read and agree to the{' '}
<Link
href="/terms-of-service"
href={`/terms-of-service?coverage=${coverageUsd}`}
target="_blank"
className="text-[#1B29FF] hover:underline font-medium"
>
Terms and Conditions
</Link>{' '}
for the DeFi Protection Security Guarantee. I understand
that only Morpho vaults are covered through this interface,
insurance (up to $1M) is provided by Chainproof (a licensed
insurer), and smart contract audits are performed by
Quantstamp.
insurance (up to {coverageDisplay}) is provided by
Chainproof (a licensed insurer), and smart contract audits
are performed by Quantstamp.
</span>
</label>
</div>
Expand Down
27 changes: 20 additions & 7 deletions packages/web/src/app/terms-of-service/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import type { Metadata } from 'next';

import {
formatCoverageUsd,
getCoverageUsdFromSearchParams,
} from '@/lib/insurance/coverage-amount';

export const metadata: Metadata = {
title: 'Terms and Conditions – DeFi Protection Security Guarantee',
description:
'Review the terms and conditions for the DeFi Protection Security Guarantee provided through 0.finance.',
robots: { index: true, follow: true },
};

export default function TermsOfServicePage() {
export default function TermsOfServicePage({
searchParams,
}: {
searchParams: Record<string, string | string[] | undefined>;
}) {
const coverageUsd = getCoverageUsdFromSearchParams(searchParams);
const coverageCompact = formatCoverageUsd(coverageUsd, { style: 'compact' });
const coverageFull = formatCoverageUsd(coverageUsd, { style: 'full' });

return (
<main className="mx-auto max-w-3xl px-4 py-16 prose prose-slate dark:prose-invert">
<h1>Terms and Conditions (DeFi Protection Security Guarantee)</h1>
Expand Down Expand Up @@ -49,9 +62,9 @@ export default function TermsOfServicePage() {
("Blockchain").
</p>
<p>
<strong>Insurance and Security:</strong> Insurance coverage (up to $1M)
is provided by Chainproof, a licensed insurer. Smart contract security
audits are performed by Quantstamp.{' '}
<strong>Insurance and Security:</strong> Insurance coverage (up to{' '}
{coverageCompact}) is provided by Chainproof, a licensed insurer. Smart
contract security audits are performed by Quantstamp.{' '}
<a href="/legal/insurance-terms" className="text-[#1B29FF] underline">
View full insurance terms
</a>
Expand All @@ -64,8 +77,8 @@ export default function TermsOfServicePage() {
accrued interest or yield, less any liabilities that you owe to the DeFi
protocol or other users of the DeFi protocol, or (ii) your chosen
Maximum Reimbursement amount (each in USD) ("Damages"). In no event will
our liability for Damages exceed $1,000,000 in the aggregate across any
one Protocol.
our liability for Damages exceed {coverageFull} in the aggregate across
any one Protocol.
</p>

<h3>Valuation Methodology</h3>
Expand Down Expand Up @@ -93,7 +106,7 @@ export default function TermsOfServicePage() {
<p>
You may only purchase a Security Services Guarantee through one Wallet
per Protocol; however, you may increase your Wallet's Maximum
Reimbursement amount at any time, up to a maximum of $1,000,000. Any
Reimbursement amount at any time, up to a maximum of {coverageFull}. Any
violation of these terms may result in the immediate cancellation of any
or all of your Contracts, and any of our obligations of reimbursement
will be void.
Expand Down
4 changes: 4 additions & 0 deletions packages/web/src/db/schema/workspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
uuid,
timestamp,
boolean,
integer,
uniqueIndex,
index,
text,
Expand Down Expand Up @@ -68,6 +69,9 @@ export const workspaces = pgTable(
withTimezone: true,
}),
insuranceActivatedBy: varchar('insurance_activated_by', { length: 255 }),
insuranceCoverageUsd: integer('insurance_coverage_usd')
.default(100_000)
.notNull(),

// AI Email Handle - human-readable email address for AI agent
// Format: ai-{firstname}.{lastname} (e.g., ai-clara.mitchell)
Expand Down
Loading