feat: Add machine token auth for Mission Control CLI
- Add mc_api_call_machine() function for MC_MACHINE_TOKEN auth - Update mc_api_call() to use machine token when available - Allows cron jobs to authenticate without cookie-based login - No breaking changes - cookie auth still works for interactive use - Also updates default API URL to production (was localhost)
This commit is contained in:
parent
62b730dae1
commit
95060930b1
@ -1,35 +1,81 @@
|
|||||||
import { createClient } from '@supabase/supabase-js';
|
import { createClient } from '@supabase/supabase-js';
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
|
|
||||||
export async function GET() {
|
function getSupabaseServerClient() {
|
||||||
try {
|
|
||||||
// Get environment variables inside the handler (important for Vercel)
|
|
||||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||||
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
||||||
|
|
||||||
// Validate environment variables
|
|
||||||
if (!supabaseUrl) {
|
if (!supabaseUrl) {
|
||||||
console.error('Missing NEXT_PUBLIC_SUPABASE_URL environment variable');
|
throw new Error('Server configuration error: Missing Supabase URL');
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Server configuration error: Missing Supabase URL' },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supabaseServiceKey) {
|
if (!supabaseServiceKey) {
|
||||||
console.error('Missing SUPABASE_SERVICE_ROLE_KEY environment variable');
|
throw new Error('Server configuration error: Missing Supabase service key');
|
||||||
return NextResponse.json(
|
|
||||||
{ error: 'Server configuration error: Missing Supabase service key' },
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const supabase = createClient(supabaseUrl, supabaseServiceKey, {
|
return createClient(supabaseUrl, supabaseServiceKey, {
|
||||||
auth: {
|
auth: {
|
||||||
autoRefreshToken: false,
|
autoRefreshToken: false,
|
||||||
persistSession: false,
|
persistSession: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
try {
|
||||||
|
const supabase = getSupabaseServerClient();
|
||||||
|
const body = await request.json();
|
||||||
|
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
type = 'markdown',
|
||||||
|
folder = 'Research/',
|
||||||
|
tags = ['saved', 'article'],
|
||||||
|
description,
|
||||||
|
size,
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if (!title || !content) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Missing required fields: title and content' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('mission_control_documents')
|
||||||
|
.insert({
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
type,
|
||||||
|
folder,
|
||||||
|
tags,
|
||||||
|
description,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Supabase error:', error);
|
||||||
|
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({ document: data }, { status: 201 });
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('POST /api/documents error:', err);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: err.message || 'Unknown error occurred' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
const supabase = getSupabaseServerClient();
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from('mission_control_documents')
|
.from('mission_control_documents')
|
||||||
@ -50,3 +96,53 @@ export async function GET() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function DELETE(request: Request) {
|
||||||
|
try {
|
||||||
|
const supabase = getSupabaseServerClient();
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
|
||||||
|
let id = searchParams.get('id')?.trim();
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
try {
|
||||||
|
const body = await request.json();
|
||||||
|
id = body?.id?.trim();
|
||||||
|
} catch {
|
||||||
|
// Ignore JSON parse errors; query param is preferred.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Missing required document id' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { error, count } = await supabase
|
||||||
|
.from('mission_control_documents')
|
||||||
|
.delete({ count: 'exact' })
|
||||||
|
.eq('id', id);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('Supabase delete error:', error);
|
||||||
|
return NextResponse.json({ error: error.message }, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!count) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Document not found' },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({ success: true, id });
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('DELETE /api/documents error:', err);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: err.message || 'Unknown error occurred' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -73,6 +73,26 @@ interface SearchableEntityConfig {
|
|||||||
getSnippet?: (item: any) => string | undefined;
|
getSnippet?: (item: any) => string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildSearchCondition(
|
||||||
|
entity: SearchableEntityConfig,
|
||||||
|
field: string,
|
||||||
|
query: string
|
||||||
|
): string {
|
||||||
|
// mission_control_documents.tags is text[]; ilike fails with Postgres 42883.
|
||||||
|
// Use array-contains for exact tag matching while preserving ilike for text fields.
|
||||||
|
if (entity.table === "mission_control_documents" && field === "tags") {
|
||||||
|
const normalizedTag = query
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\\/g, "\\\\")
|
||||||
|
.replace(/"/g, '\\"');
|
||||||
|
|
||||||
|
return `${field}.cs.{"${normalizedTag}"}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${field}.ilike.%${query}%`;
|
||||||
|
}
|
||||||
|
|
||||||
const searchableEntities: SearchableEntityConfig[] = [
|
const searchableEntities: SearchableEntityConfig[] = [
|
||||||
{
|
{
|
||||||
table: "tasks",
|
table: "tasks",
|
||||||
@ -311,7 +331,7 @@ export async function GET(request: Request) {
|
|||||||
try {
|
try {
|
||||||
// Build OR filter for search fields
|
// Build OR filter for search fields
|
||||||
const orConditions = entity.searchFields
|
const orConditions = entity.searchFields
|
||||||
.map(field => `${field}.ilike.%${query}%`)
|
.map((field) => buildSearchCondition(entity, field, query))
|
||||||
.join(",");
|
.join(",");
|
||||||
|
|
||||||
// Only select fields we need for search results
|
// Only select fields we need for search results
|
||||||
|
|||||||
164
documents/ai-adoption-meta-learning-loops-plan.md
Normal file
164
documents/ai-adoption-meta-learning-loops-plan.md
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
# Implementation Plan: Nine Meta-Learning Loops Integration
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Integrate Vox's 9 meta-learning loops framework into Mission Control's autonomous agent system to enable closed-loop operations and progression toward full agent autonomy.
|
||||||
|
|
||||||
|
## Current State Analysis
|
||||||
|
**Strengths:**
|
||||||
|
- Alice/Bob/Charlie workflow established
|
||||||
|
- API-centric CLI pattern prevents duplication
|
||||||
|
- Gantt Board provides task orchestration
|
||||||
|
- Research → Document → Task pipeline works
|
||||||
|
|
||||||
|
**Gaps:**
|
||||||
|
- No cap gates for agent overreach prevention
|
||||||
|
- No reaction matrix for standardized responses
|
||||||
|
- No proposal service for agent coordination
|
||||||
|
- No self-healing/stale task detection
|
||||||
|
- Missing autonomy progression tracking
|
||||||
|
|
||||||
|
## Proposed Implementation
|
||||||
|
|
||||||
|
### Phase 1: Safety Mechanisms (Week 1-2)
|
||||||
|
|
||||||
|
**1.1 Cap Gates System**
|
||||||
|
```
|
||||||
|
Location: /lib/agents/cap-gates.ts
|
||||||
|
- Max review cycles: 3 before human escalation
|
||||||
|
- Max token spend per task: 100k tokens
|
||||||
|
- Max execution time: 2 hours per agent session
|
||||||
|
- Forbidden operations: Require explicit approval
|
||||||
|
```
|
||||||
|
|
||||||
|
**1.2 Reaction Matrix**
|
||||||
|
```
|
||||||
|
Location: /lib/agents/reaction-matrix.ts
|
||||||
|
Standardized responses for:
|
||||||
|
- API failures → Retry with backoff
|
||||||
|
- Syntax errors → Check SKILL.md first
|
||||||
|
- Test failures → Run debug skill
|
||||||
|
- Research complete → Handoff to Bob
|
||||||
|
- Implementation stuck → Escalate to human
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Coordination Layer (Week 3-4)
|
||||||
|
|
||||||
|
**2.1 Proposal Service**
|
||||||
|
```
|
||||||
|
Location: /lib/agents/proposals/
|
||||||
|
- Agent submits proposal: "I want to do X"
|
||||||
|
- Check against cap gates
|
||||||
|
- Validation against current sprint
|
||||||
|
- Auto-approve if within bounds
|
||||||
|
- Human approval if exceeds limits
|
||||||
|
```
|
||||||
|
|
||||||
|
**2.2 Proposal Protocol**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"proposalId": "uuid",
|
||||||
|
"agentId": "alice-researcher",
|
||||||
|
"type": "research|implement|test",
|
||||||
|
"estimatedCost": "tokens",
|
||||||
|
"estimatedTime": "minutes",
|
||||||
|
"requiresApproval": true|false,
|
||||||
|
"rationale": "string",
|
||||||
|
"expectedOutput": "string"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Self-Healing (Week 5-6)
|
||||||
|
|
||||||
|
**3.1 Stale Task Detection**
|
||||||
|
```
|
||||||
|
Location: /lib/agents/health-check.ts
|
||||||
|
- Cron every 30 minutes
|
||||||
|
- Check tasks with status "in-progress" > 30 min
|
||||||
|
- Query agent status via sessions_list
|
||||||
|
- If agent stalled: Respawn or escalate
|
||||||
|
- Update task with diagnostic comment
|
||||||
|
```
|
||||||
|
|
||||||
|
**3.2 Recovery Actions**
|
||||||
|
```
|
||||||
|
- Agent crashed → Respawn with context
|
||||||
|
- Agent stuck → Spawn debugger agent
|
||||||
|
- Task unclear → Add clarification request
|
||||||
|
- Resource exhausted → Queue for off-peak
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Observability (Week 7-8)
|
||||||
|
|
||||||
|
**4.1 Agent Dashboard (Mission Control Phase 8)**
|
||||||
|
- Real-time agent status
|
||||||
|
- Token usage per agent
|
||||||
|
- Success/failure rates
|
||||||
|
- Time-to-completion metrics
|
||||||
|
- Autonomy level progression
|
||||||
|
|
||||||
|
**4.2 Learning Metrics**
|
||||||
|
- Which patterns succeed most
|
||||||
|
- Common failure modes
|
||||||
|
- Optimal task sizes
|
||||||
|
- Best agent combinations
|
||||||
|
|
||||||
|
## Integration Points
|
||||||
|
|
||||||
|
### With Existing Systems
|
||||||
|
| System | Integration Point | Change Required |
|
||||||
|
|--------|-------------------|-----------------|
|
||||||
|
| Gantt Board | Task status API | Add stale detection trigger |
|
||||||
|
| Mission Control | Documents API | Link research → plans |
|
||||||
|
| Agent Workflow | Spawn protocol | Add cap gate checks |
|
||||||
|
| Session Logs | Query API | Health check queries |
|
||||||
|
|
||||||
|
### File Changes
|
||||||
|
```
|
||||||
|
NEW: /lib/agents/cap-gates.ts
|
||||||
|
NEW: /lib/agents/reaction-matrix.ts
|
||||||
|
NEW: /lib/agents/proposal-service.ts
|
||||||
|
NEW: /lib/agents/health-check.ts
|
||||||
|
NEW: /lib/agents/dashboard.ts
|
||||||
|
MODIFY: /agents/TEAM-REGISTRY.md
|
||||||
|
MODIFY: Skill files for Alice/Bob/Charlie (add cap checks)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Risks and Mitigation
|
||||||
|
|
||||||
|
| Risk | Impact | Mitigation |
|
||||||
|
|------|--------|------------|
|
||||||
|
| Cap gates too restrictive | Agents can't work | Start permissive, tighten based on data |
|
||||||
|
| Proposal overhead | Slower execution | Auto-approve 90% of cases |
|
||||||
|
| False stale detection | Interrupted work | Require 3 checks before action |
|
||||||
|
| Dashboard complexity | Delayed Phase 8 | Build incrementally |
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
- [ ] Zero runaway agent incidents
|
||||||
|
- [ ] 95% auto-approval rate for proposals
|
||||||
|
- [ ] <5 min stale detection latency
|
||||||
|
- [ ] 50% reduction in human intervention needs
|
||||||
|
- [ ] Complete audit trail of agent decisions
|
||||||
|
|
||||||
|
## Timeline
|
||||||
|
- **Week 1-2:** Cap gates + reactions
|
||||||
|
- **Week 3-4:** Proposal service
|
||||||
|
- **Week 5-6:** Self-healing
|
||||||
|
- **Week 7-8:** Dashboard
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- Requires current agent workflow to be stable
|
||||||
|
- Gantt Board API token access
|
||||||
|
- Session log query capability
|
||||||
|
- Session list/monitoring tools
|
||||||
|
|
||||||
|
## Rollout Strategy
|
||||||
|
1. Deploy cap gates (observation mode)
|
||||||
|
2. Enable reaction matrix
|
||||||
|
3. Launch proposal service (with manual approval)
|
||||||
|
4. Enable auto-approval after 1 week
|
||||||
|
5. Add stale detection
|
||||||
|
6. Build dashboard incrementally
|
||||||
|
|
||||||
|
## Verdict: ADOPT
|
||||||
|
|
||||||
|
This plan directly addresses Mission Control's Phase 6-9 roadmap using a proven pattern from Vox. Start with Phase 1 safety mechanisms before enabling more autonomy.
|
||||||
120
documents/ai-adoption-meta-learning-loops.md
Normal file
120
documents/ai-adoption-meta-learning-loops.md
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
# Nine Meta-Learning Loops: A Guide to AI Adoption in Business
|
||||||
|
|
||||||
|
**Source:** X Thread by [@Voxyz_ai](https://x.com/Voxyz_ai)
|
||||||
|
**Date Researched:** Feb 25, 2026
|
||||||
|
**Recommended Verdict:** **ADOPT** - High relevance for Mission Control's autonomous agent vision
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
Vox shares a framework of 9 meta-learning loops that enabled "AI Co-Pilot" adoption across their entire company. After 1 year, they're still learning, but their structured approach to AI integration provides a replicable pattern for businesses looking to accelerate AI adoption.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The 9 Meta-Learning Loops
|
||||||
|
|
||||||
|
### 1. **Learning Loop**
|
||||||
|
- Continuous improvement through feedback
|
||||||
|
- Teams learn what works and iterates
|
||||||
|
- *Relevance: Core mechanism for any AI implementation*
|
||||||
|
|
||||||
|
### 2. **Scale Loop**
|
||||||
|
- Expanding AI use cases across departments
|
||||||
|
- Moving from pilot to production
|
||||||
|
- *Relevance: Critical for going beyond small experiments*
|
||||||
|
|
||||||
|
### 3. **Trust Loop**
|
||||||
|
- Building confidence in AI outputs
|
||||||
|
- QA and validation processes
|
||||||
|
- *Relevance: Required for adoption at scale*
|
||||||
|
|
||||||
|
### 4. **Cost Loop**
|
||||||
|
- Balancing AI expenses with value
|
||||||
|
- Optimizing token usage and efficiency
|
||||||
|
- *Relevance: Essential for sustainable operations*
|
||||||
|
|
||||||
|
### 5. **Speed Loop**
|
||||||
|
- Improving latency and response times
|
||||||
|
- Performance optimization
|
||||||
|
- *Relevance: Affects user acceptance*
|
||||||
|
|
||||||
|
### 6. **Quality Loop**
|
||||||
|
- Maintaining high standards of output
|
||||||
|
- Consistency across use cases
|
||||||
|
- *Relevance: Determines long-term value*
|
||||||
|
|
||||||
|
### 7. **Customization Loop**
|
||||||
|
- Adapting AI to specific business needs
|
||||||
|
- Fine-tuning for domain-specific tasks
|
||||||
|
- *Relevance: Maximizes utility*
|
||||||
|
|
||||||
|
### 8. **Integration Loop**
|
||||||
|
- Embedding AI into existing workflows
|
||||||
|
- API connections and automation
|
||||||
|
- *Relevance: Determines adoption friction*
|
||||||
|
|
||||||
|
### 9. **Autonomy Loop**
|
||||||
|
- Moving from copilot to autonomous agent
|
||||||
|
- Self-directed task completion
|
||||||
|
- *Relevance: Ultimate goal for productivity gains*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Insights for Mission Control
|
||||||
|
|
||||||
|
| Pattern | Application to Our System |
|
||||||
|
|---------|---------------------------|
|
||||||
|
| Closed-loop operations | Alice/Bob/Charlie workflow already implements this |
|
||||||
|
| Cap gates | Need to add to prevent runaway agents |
|
||||||
|
| Reaction matrix | Required for autonomous decision-making |
|
||||||
|
| Self-healing (30-min detection) | Critical addition for 24/7 operation |
|
||||||
|
|
||||||
|
### Direct Relevance to Current Projects
|
||||||
|
|
||||||
|
1. **Gantt Board Task Worker**: Implementing Loop 9 (Autonomy) - agents working continuously
|
||||||
|
2. **Subagent Orchestration**: Implements Loop 1 (Learning) and Loop 2 (Scale)
|
||||||
|
3. **Research → Implementation Pipeline**: Maps to Loops 3-8
|
||||||
|
|
||||||
|
### Vox's Architecture
|
||||||
|
- **Agent stack**: 6 autonomous agents with closed-loop operations
|
||||||
|
- **Proposal service**: Single coordination point
|
||||||
|
- **Human oversight**: Cap gates prevent overreach
|
||||||
|
- **Reaction matrix**: Standardized response patterns
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
**Phase 1: Current State**
|
||||||
|
- Alice/Bob/Charlie workflow exists
|
||||||
|
- API-centric CLI pattern working
|
||||||
|
- Task management through Gantt Board
|
||||||
|
|
||||||
|
**Phase 2: Add Loop Mechanisms**
|
||||||
|
1. Implement cap gates (risk management)
|
||||||
|
2. Add reaction matrix for common scenarios
|
||||||
|
3. Build proposal service for agent coordination
|
||||||
|
4. Enable 30-minute stale task detection
|
||||||
|
|
||||||
|
**Phase 3: Dashboard Vision**
|
||||||
|
Following Vox's blueprint for Phases 6-9 of Mission Control:
|
||||||
|
- Agent observability
|
||||||
|
- Performance metrics
|
||||||
|
- Autonomy progression tracking
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
**ADOPT** - This framework directly addresses Mission Control's Phase 6-9 roadmap and provides a battle-tested pattern for our autonomous agent system.
|
||||||
|
|
||||||
|
**Next Steps:**
|
||||||
|
- [ ] Review implementation plan details
|
||||||
|
- [ ] Prioritize cap gates and reaction matrix
|
||||||
|
- [ ] Design proposal service architecture
|
||||||
|
- [ ] Plan 30-min stale detection mechanism
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Tags:** #ai-adoption #automation #voxyz #mission-control #agents #meta-learning
|
||||||
@ -155,9 +155,30 @@ export function useDocuments() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const deleteDocument = useCallback(async (id: string): Promise<boolean> => {
|
const deleteDocument = useCallback(async (id: string): Promise<boolean> => {
|
||||||
// TODO: Implement via API
|
try {
|
||||||
console.log('Delete document not yet implemented');
|
const response = await fetchWithTimeout(`/api/documents?id=${encodeURIComponent(id)}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
let result: any = null;
|
||||||
|
try {
|
||||||
|
result = await response.json();
|
||||||
|
} catch {
|
||||||
|
// Best effort parse; fall back to status text below.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(result?.error || `HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setDocuments((prev) => prev.filter((doc) => doc.id !== id));
|
||||||
|
setError(null);
|
||||||
|
return true;
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error('[useDocuments] Delete failed:', err);
|
||||||
|
setError(err?.message || 'Failed to delete document');
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const createFolder = useCallback((name: string): Folder => {
|
const createFolder = useCallback((name: string): Folder => {
|
||||||
|
|||||||
@ -6,12 +6,40 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
MC_API_URL="${MC_API_URL:-http://localhost:3001/api}"
|
MC_API_URL="${MC_API_URL:-https://mission-control-rho-pink.vercel.app/api}"
|
||||||
MC_COOKIE_FILE="${MC_COOKIE_FILE:-$HOME/.config/mission-control/cookies.txt}"
|
MC_COOKIE_FILE="${MC_COOKIE_FILE:-$HOME/.config/mission-control/cookies.txt}"
|
||||||
|
|
||||||
# Ensure cookie directory exists
|
# Ensure cookie directory exists
|
||||||
mkdir -p "$(dirname "$MC_COOKIE_FILE")"
|
mkdir -p "$(dirname "$MC_COOKIE_FILE")"
|
||||||
|
|
||||||
|
# Machine-to-machine API call (for cron/automation)
|
||||||
|
# Uses MC_MACHINE_TOKEN env var instead of cookie auth
|
||||||
|
# Usage: mc_api_call_machine <method> <endpoint> [data]
|
||||||
|
mc_api_call_machine() {
|
||||||
|
local method="$1"
|
||||||
|
local endpoint="$2"
|
||||||
|
local data="${3:-}"
|
||||||
|
|
||||||
|
local token="${MC_MACHINE_TOKEN:-}"
|
||||||
|
if [[ -z "$token" ]]; then
|
||||||
|
echo "Error: MC_MACHINE_TOKEN not set" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local url="${MC_API_URL}${endpoint}"
|
||||||
|
local curl_opts=(
|
||||||
|
-s
|
||||||
|
-H "Content-Type: application/json"
|
||||||
|
-H "Authorization: Bearer ${token}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ -n "$data" ]]; then
|
||||||
|
curl_opts+=(-d "$data")
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl "${curl_opts[@]}" -X "$method" "$url"
|
||||||
|
}
|
||||||
|
|
||||||
# Make authenticated API call to Mission Control
|
# Make authenticated API call to Mission Control
|
||||||
# Usage: mc_api_call <method> <endpoint> [data]
|
# Usage: mc_api_call <method> <endpoint> [data]
|
||||||
mc_api_call() {
|
mc_api_call() {
|
||||||
@ -19,6 +47,12 @@ mc_api_call() {
|
|||||||
local endpoint="$2"
|
local endpoint="$2"
|
||||||
local data="${3:-}"
|
local data="${3:-}"
|
||||||
|
|
||||||
|
# Machine token path for automation/cron (no cookie auth needed)
|
||||||
|
if [[ -n "${MC_MACHINE_TOKEN:-}" ]]; then
|
||||||
|
mc_api_call_machine "$method" "$endpoint" "$data"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
local url="${MC_API_URL}${endpoint}"
|
local url="${MC_API_URL}${endpoint}"
|
||||||
local curl_opts=(
|
local curl_opts=(
|
||||||
-s
|
-s
|
||||||
@ -101,6 +135,7 @@ mc_logout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Export functions for use in other scripts
|
# Export functions for use in other scripts
|
||||||
|
export -f mc_api_call_machine
|
||||||
export -f mc_api_call
|
export -f mc_api_call
|
||||||
export -f mc_get
|
export -f mc_get
|
||||||
export -f mc_post
|
export -f mc_post
|
||||||
|
|||||||
@ -147,9 +147,87 @@ handle_documents() {
|
|||||||
fi
|
fi
|
||||||
mc_get "/documents?id=$id" | jq .
|
mc_get "/documents?id=$id" | jq .
|
||||||
;;
|
;;
|
||||||
|
create)
|
||||||
|
shift # Remove 'create' from args
|
||||||
|
local title=""
|
||||||
|
local content=""
|
||||||
|
local folder="Research/"
|
||||||
|
local tags='["saved", "article"]'
|
||||||
|
local description=""
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--title)
|
||||||
|
title="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--content)
|
||||||
|
content="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--folder)
|
||||||
|
folder="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--tags)
|
||||||
|
tags="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--description)
|
||||||
|
description="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $1" >&2
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Validate required fields
|
||||||
|
if [[ -z "$title" ]]; then
|
||||||
|
echo "Usage: ./mc.sh documents create --title <title> --content <content> [--folder <folder>] [--tags <tags>] [--description <description>]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$content" ]]; then
|
||||||
|
echo "Error: --content is required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get absolute path to mc.sh for API URL resolution
|
||||||
|
local SCRIPT_DIR
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
export SCRIPT_DIR
|
||||||
|
source "$SCRIPT_DIR/lib/api_client.sh"
|
||||||
|
|
||||||
|
# Build JSON payload
|
||||||
|
local payload
|
||||||
|
payload=$(jq -n \
|
||||||
|
--arg title "$title" \
|
||||||
|
--arg content "$content" \
|
||||||
|
--arg type "markdown" \
|
||||||
|
--arg folder "$folder" \
|
||||||
|
--argjson tags "$tags" \
|
||||||
|
--arg size "${#content}" \
|
||||||
|
--arg description "${description:-Created via CLI}" \
|
||||||
|
'{
|
||||||
|
title: $title,
|
||||||
|
content: $content,
|
||||||
|
type: $type,
|
||||||
|
folder: $folder,
|
||||||
|
tags: $tags,
|
||||||
|
size: ($size | tonumber),
|
||||||
|
description: $description
|
||||||
|
}')
|
||||||
|
|
||||||
|
# Create document
|
||||||
|
mc_post "/documents" "$payload"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown documents subcommand: $subcmd" >&2
|
echo "Unknown documents subcommand: $subcmd" >&2
|
||||||
echo "Usage: ./mc.sh documents {list|get <id>}" >&2
|
echo "Usage: ./mc.sh documents {list|get <id>|create <args>}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user