Upload 18 files
Browse files- .gitignore +7 -0
- LICENSE +21 -0
- README.md +111 -0
- client/index.html +153 -0
- client/src/main.js +491 -0
- client/src/style.css +888 -0
- output/.gitkeep +2 -0
- output/Q-TpLKXDEEo-transcript.txt +0 -0
- output/X-Dwe-s9URU-transcript.txt +184 -0
- output/X-Dwe-s9URU.en.vtt +1472 -0
- package-lock.json +1078 -0
- package.json +27 -0
- server/bridge.ts +285 -0
- server/cliDetector.ts +195 -0
- server/index.ts +125 -0
- server/toolRegistry.ts +109 -0
- server/tools/transcript.ts +493 -0
- tsconfig.json +25 -0
.gitignore
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules/
|
| 2 |
+
output/
|
| 3 |
+
*.log
|
| 4 |
+
.env
|
| 5 |
+
.DS_Store
|
| 6 |
+
Thumbs.db
|
| 7 |
+
settings.json
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Rembrant Oyangoren Albeos
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Agent Bridge
|
| 2 |
+
|
| 3 |
+
A TypeScript framework for bridging a web application to the Google Antigravity CLI. Single-page interface with MCP-like tool system and real-time agent communication.
|
| 4 |
+
|
| 5 |
+
## Architecture
|
| 6 |
+
|
| 7 |
+
```
|
| 8 |
+
Browser (Single Page) <--WebSocket--> Node/Express Server <--child_process--> Antigravity CLI
|
| 9 |
+
|
|
| 10 |
+
Tool Registry
|
| 11 |
+
(MCP-like system)
|
| 12 |
+
```
|
| 13 |
+
|
| 14 |
+
## Quick Start
|
| 15 |
+
|
| 16 |
+
```bash
|
| 17 |
+
cd c:\Users\User\Desktop\debugrem\agent-bridge
|
| 18 |
+
npm install
|
| 19 |
+
npm start
|
| 20 |
+
# Open http://localhost:3777
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
## Project Structure
|
| 24 |
+
|
| 25 |
+
```
|
| 26 |
+
agent-bridge/
|
| 27 |
+
server/
|
| 28 |
+
index.ts Entry point (Express + Socket.IO)
|
| 29 |
+
bridge.ts Unified WebSocket event hub
|
| 30 |
+
toolRegistry.ts Typed MCP-like tool system
|
| 31 |
+
cliDetector.ts Real multi-strategy CLI auto-detection
|
| 32 |
+
tools/
|
| 33 |
+
transcript.ts YouTube URL to transcript (yt-dlp + API fallback)
|
| 34 |
+
client/
|
| 35 |
+
index.html Single-page app
|
| 36 |
+
src/
|
| 37 |
+
style.css Light theme, dot-grid canvas, Google aesthetic
|
| 38 |
+
main.js WebSocket, chat UI, tool rail, suggestion chips
|
| 39 |
+
output/ Generated files
|
| 40 |
+
tsconfig.json
|
| 41 |
+
package.json
|
| 42 |
+
```
|
| 43 |
+
|
| 44 |
+
## CLI Auto-Detection
|
| 45 |
+
|
| 46 |
+
The server automatically searches for the Antigravity CLI using 5 strategies:
|
| 47 |
+
|
| 48 |
+
| Priority | Method | Location |
|
| 49 |
+
|---|---|---|
|
| 50 |
+
| 1 | Environment variable | `ANTIGRAVITY_CLI_PATH` |
|
| 51 |
+
| 2 | System PATH | `where antigravity` / `which antigravity` |
|
| 52 |
+
| 3 | Common install paths | AppData, Program Files, ~/.local/bin |
|
| 53 |
+
| 4 | npm globals | `npm root -g` parent directory |
|
| 54 |
+
| 5 | Bare execution | Direct `antigravity --version` probe |
|
| 55 |
+
|
| 56 |
+
No mock status. The detection result is **real** -- if it says "not detected", the CLI is genuinely not found.
|
| 57 |
+
|
| 58 |
+
## Configuration
|
| 59 |
+
|
| 60 |
+
| Variable | Default | Description |
|
| 61 |
+
|---|---|---|
|
| 62 |
+
| `PORT` | `3777` | Server port |
|
| 63 |
+
| `ANTIGRAVITY_CLI_PATH` | `antigravity` | Override CLI binary path |
|
| 64 |
+
|
| 65 |
+
## Tool System
|
| 66 |
+
|
| 67 |
+
Drop a `.ts` file in `server/tools/` with a `register(registry)` export. The server auto-loads it on startup.
|
| 68 |
+
|
| 69 |
+
```typescript
|
| 70 |
+
import type { ToolRegistry } from '../toolRegistry';
|
| 71 |
+
|
| 72 |
+
export function register(registry: ToolRegistry): void {
|
| 73 |
+
registry.register({
|
| 74 |
+
name: 'my_tool',
|
| 75 |
+
description: 'Does something useful',
|
| 76 |
+
syntax: 'use <my_tool> <param>',
|
| 77 |
+
pattern: /use\s+<my_tool>\s+(?<param>.+)/i,
|
| 78 |
+
mock: false,
|
| 79 |
+
async execute(params, emitProgress) {
|
| 80 |
+
emitProgress('Working...');
|
| 81 |
+
return { message: 'Done' };
|
| 82 |
+
},
|
| 83 |
+
});
|
| 84 |
+
}
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
## WebSocket Events
|
| 88 |
+
|
| 89 |
+
| Event | Direction | Description |
|
| 90 |
+
|---|---|---|
|
| 91 |
+
| `bridge:init` | Server -> Client | Initial state (tools, CLI status) |
|
| 92 |
+
| `prompt:send` | Client -> Server | User sends a message |
|
| 93 |
+
| `tool:started/progress/completed/error` | Server -> All | Tool lifecycle |
|
| 94 |
+
| `tool:list` | Client -> Server | Query tools (callback) |
|
| 95 |
+
| `tool:invoke` | Client -> Server | Invoke a tool (callback) |
|
| 96 |
+
| `agent:message` | Bidirectional | Agent communication |
|
| 97 |
+
| `cli:stdout/stderr/done` | Server -> All | CLI output streaming |
|
| 98 |
+
|
| 99 |
+
## Usage Example
|
| 100 |
+
|
| 101 |
+
Type in the chatbox:
|
| 102 |
+
|
| 103 |
+
```
|
| 104 |
+
use <transcript_tool> https://www.youtube.com/watch?v=y8KofLWrkeU
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
The tool extracts subtitles and provides a downloadable transcript.
|
| 108 |
+
|
| 109 |
+
## License
|
| 110 |
+
|
| 111 |
+
MIT -- Rembrant Oyangoren Albeos, 2026
|
client/index.html
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
|
| 4 |
+
<head>
|
| 5 |
+
<meta charset="UTF-8">
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
+
<meta name="description" content="Agent Bridge -- Webapp-to-Antigravity-CLI bridging framework">
|
| 8 |
+
<title>Agent Bridge</title>
|
| 9 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 10 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 11 |
+
<link
|
| 12 |
+
href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=JetBrains+Mono:wght@400;500&display=swap"
|
| 13 |
+
rel="stylesheet">
|
| 14 |
+
<link rel="stylesheet" href="src/style.css">
|
| 15 |
+
</head>
|
| 16 |
+
|
| 17 |
+
<body>
|
| 18 |
+
|
| 19 |
+
<div id="app">
|
| 20 |
+
<!-- Left Tool Buttons -->
|
| 21 |
+
<aside id="tool-rail"></aside>
|
| 22 |
+
|
| 23 |
+
<!-- Main Canvas -->
|
| 24 |
+
<main id="canvas">
|
| 25 |
+
<!-- Status bar -->
|
| 26 |
+
<div id="status-bar">
|
| 27 |
+
<span id="connection-indicator"></span>
|
| 28 |
+
<span id="status-text">Connecting...</span>
|
| 29 |
+
<button id="settings-toggle" class="settings-btn" title="Settings">
|
| 30 |
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
| 31 |
+
stroke-linecap="round" stroke-linejoin="round">
|
| 32 |
+
<circle cx="12" cy="12" r="3"></circle>
|
| 33 |
+
<path
|
| 34 |
+
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z">
|
| 35 |
+
</path>
|
| 36 |
+
</svg>
|
| 37 |
+
</button>
|
| 38 |
+
</div>
|
| 39 |
+
|
| 40 |
+
<!-- Settings Panel (hidden by default) -->
|
| 41 |
+
<div id="settings-panel" class="hidden">
|
| 42 |
+
<div class="settings-card">
|
| 43 |
+
<div class="settings-header">
|
| 44 |
+
<h2>Antigravity Settings</h2>
|
| 45 |
+
<button id="settings-close" aria-label="Close">×</button>
|
| 46 |
+
</div>
|
| 47 |
+
|
| 48 |
+
<div class="settings-body">
|
| 49 |
+
<label class="settings-label">CLI Executable Path</label>
|
| 50 |
+
<div class="settings-input-row">
|
| 51 |
+
<input type="text" id="cli-path-input"
|
| 52 |
+
placeholder="e.g. C:\Users\You\AppData\Local\Programs\antigravity\antigravity.exe" spellcheck="false">
|
| 53 |
+
<button id="save-path-btn" class="btn-primary">Save</button>
|
| 54 |
+
</div>
|
| 55 |
+
<p class="settings-hint">Full path to the Antigravity CLI binary on your computer.</p>
|
| 56 |
+
|
| 57 |
+
<div class="settings-divider"></div>
|
| 58 |
+
|
| 59 |
+
<label class="settings-label">Detection Status</label>
|
| 60 |
+
<div id="detection-status" class="detection-box">
|
| 61 |
+
<span id="detect-indicator" class="detect-dot"></span>
|
| 62 |
+
<span id="detect-text">Checking...</span>
|
| 63 |
+
</div>
|
| 64 |
+
<div id="detect-details" class="detect-details"></div>
|
| 65 |
+
|
| 66 |
+
<button id="redetect-btn" class="btn-outline">Re-detect CLI</button>
|
| 67 |
+
|
| 68 |
+
<div class="settings-divider"></div>
|
| 69 |
+
|
| 70 |
+
<label class="settings-label">CLI Session</label>
|
| 71 |
+
<div class="settings-actions">
|
| 72 |
+
<button id="connect-btn" class="btn-primary">Connect to CLI</button>
|
| 73 |
+
<button id="disconnect-btn" class="btn-outline btn-danger" disabled>Disconnect</button>
|
| 74 |
+
</div>
|
| 75 |
+
<p id="session-status" class="settings-hint">No active session.</p>
|
| 76 |
+
</div>
|
| 77 |
+
</div>
|
| 78 |
+
</div>
|
| 79 |
+
|
| 80 |
+
<!-- Center Card (initial state) -->
|
| 81 |
+
<div id="center-card">
|
| 82 |
+
<button id="card-close" aria-label="Close">×</button>
|
| 83 |
+
<h1 id="greeting">Hey, what are we working on today?</h1>
|
| 84 |
+
|
| 85 |
+
<div id="prompt-container">
|
| 86 |
+
<input type="text" id="prompt-input" placeholder="I want to create..." autocomplete="off" spellcheck="false">
|
| 87 |
+
<button id="send-btn" title="Send">
|
| 88 |
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
|
| 89 |
+
stroke-linecap="round" stroke-linejoin="round">
|
| 90 |
+
<line x1="5" y1="12" x2="19" y2="12"></line>
|
| 91 |
+
<polyline points="12 5 19 12 12 19"></polyline>
|
| 92 |
+
</svg>
|
| 93 |
+
</button>
|
| 94 |
+
</div>
|
| 95 |
+
|
| 96 |
+
<div id="icon-bar"></div>
|
| 97 |
+
|
| 98 |
+
<div id="suggestion-chips"></div>
|
| 99 |
+
|
| 100 |
+
<div id="bottom-links">
|
| 101 |
+
<a href="#" id="link-tools" class="bottom-link">
|
| 102 |
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
| 103 |
+
<rect x="3" y="3" width="7" height="7"></rect>
|
| 104 |
+
<rect x="14" y="3" width="7" height="7"></rect>
|
| 105 |
+
<rect x="14" y="14" width="7" height="7"></rect>
|
| 106 |
+
<rect x="3" y="14" width="7" height="7"></rect>
|
| 107 |
+
</svg>
|
| 108 |
+
Explore Tools
|
| 109 |
+
</a>
|
| 110 |
+
<a href="#" id="link-bridge" class="bottom-link">
|
| 111 |
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
| 112 |
+
<circle cx="12" cy="12" r="10"></circle>
|
| 113 |
+
<line x1="2" y1="12" x2="22" y2="12"></line>
|
| 114 |
+
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z">
|
| 115 |
+
</path>
|
| 116 |
+
</svg>
|
| 117 |
+
Bridge Status
|
| 118 |
+
</a>
|
| 119 |
+
</div>
|
| 120 |
+
</div>
|
| 121 |
+
|
| 122 |
+
<!-- Conversation view -->
|
| 123 |
+
<div id="conversation" class="hidden">
|
| 124 |
+
<div id="messages"></div>
|
| 125 |
+
|
| 126 |
+
<div id="output-area" class="hidden">
|
| 127 |
+
<div id="output-header">
|
| 128 |
+
<span>Output</span>
|
| 129 |
+
<button id="close-output" aria-label="Close">×</button>
|
| 130 |
+
</div>
|
| 131 |
+
<div id="output-content"></div>
|
| 132 |
+
</div>
|
| 133 |
+
|
| 134 |
+
<div id="conv-input-bar">
|
| 135 |
+
<input type="text" id="conv-input" placeholder="Type a prompt or tool command..." autocomplete="off"
|
| 136 |
+
spellcheck="false">
|
| 137 |
+
<button id="conv-send-btn" title="Send">
|
| 138 |
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
|
| 139 |
+
stroke-linecap="round" stroke-linejoin="round">
|
| 140 |
+
<line x1="5" y1="12" x2="19" y2="12"></line>
|
| 141 |
+
<polyline points="12 5 19 12 12 19"></polyline>
|
| 142 |
+
</svg>
|
| 143 |
+
</button>
|
| 144 |
+
</div>
|
| 145 |
+
</div>
|
| 146 |
+
</main>
|
| 147 |
+
</div>
|
| 148 |
+
|
| 149 |
+
<script src="/socket.io/socket.io.js"></script>
|
| 150 |
+
<script src="src/main.js"></script>
|
| 151 |
+
</body>
|
| 152 |
+
|
| 153 |
+
</html>
|
client/src/main.js
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ---------------------------------------------------------------------------
|
| 2 |
+
// Agent Bridge -- Frontend Application (v2.1)
|
| 3 |
+
// ---------------------------------------------------------------------------
|
| 4 |
+
// Handles WebSocket, settings panel, honest CLI status, real chat relay,
|
| 5 |
+
// tool rail, suggestion chips, and output rendering.
|
| 6 |
+
// ---------------------------------------------------------------------------
|
| 7 |
+
|
| 8 |
+
(function () {
|
| 9 |
+
'use strict';
|
| 10 |
+
|
| 11 |
+
// -----------------------------------------------------------------------
|
| 12 |
+
// DOM
|
| 13 |
+
// -----------------------------------------------------------------------
|
| 14 |
+
const $ = (id) => document.getElementById(id);
|
| 15 |
+
|
| 16 |
+
const $toolRail = $('tool-rail');
|
| 17 |
+
const $centerCard = $('center-card');
|
| 18 |
+
const $greeting = $('greeting');
|
| 19 |
+
const $promptInput = $('prompt-input');
|
| 20 |
+
const $sendBtn = $('send-btn');
|
| 21 |
+
const $iconBar = $('icon-bar');
|
| 22 |
+
const $chips = $('suggestion-chips');
|
| 23 |
+
const $connInd = $('connection-indicator');
|
| 24 |
+
const $statusText = $('status-text');
|
| 25 |
+
const $cardClose = $('card-close');
|
| 26 |
+
const $settingsToggle = $('settings-toggle');
|
| 27 |
+
|
| 28 |
+
// Settings panel
|
| 29 |
+
const $settingsPanel = $('settings-panel');
|
| 30 |
+
const $settingsClose = $('settings-close');
|
| 31 |
+
const $cliPathInput = $('cli-path-input');
|
| 32 |
+
const $savePathBtn = $('save-path-btn');
|
| 33 |
+
const $detectIndicator = $('detect-indicator');
|
| 34 |
+
const $detectText = $('detect-text');
|
| 35 |
+
const $detectDetails = $('detect-details');
|
| 36 |
+
const $redetectBtn = $('redetect-btn');
|
| 37 |
+
const $connectBtn = $('connect-btn');
|
| 38 |
+
const $disconnectBtn = $('disconnect-btn');
|
| 39 |
+
const $sessionStatus = $('session-status');
|
| 40 |
+
|
| 41 |
+
// Conversation
|
| 42 |
+
const $conversation = $('conversation');
|
| 43 |
+
const $messages = $('messages');
|
| 44 |
+
const $convInput = $('conv-input');
|
| 45 |
+
const $convSendBtn = $('conv-send-btn');
|
| 46 |
+
const $outputArea = $('output-area');
|
| 47 |
+
const $outputContent = $('output-content');
|
| 48 |
+
const $closeOutput = $('close-output');
|
| 49 |
+
const $linkTools = $('link-tools');
|
| 50 |
+
const $linkBridge = $('link-bridge');
|
| 51 |
+
|
| 52 |
+
// -----------------------------------------------------------------------
|
| 53 |
+
// State
|
| 54 |
+
// -----------------------------------------------------------------------
|
| 55 |
+
let socket = null;
|
| 56 |
+
let tools = [];
|
| 57 |
+
let msgCount = 0;
|
| 58 |
+
let inConversation = false;
|
| 59 |
+
let cliInfo = { detected: false, version: null, path: null, method: null };
|
| 60 |
+
let sessionActive = false;
|
| 61 |
+
let savedSettings = { cliPath: '', autoConnect: false };
|
| 62 |
+
|
| 63 |
+
// -----------------------------------------------------------------------
|
| 64 |
+
// Connect
|
| 65 |
+
// -----------------------------------------------------------------------
|
| 66 |
+
function connect() {
|
| 67 |
+
socket = io({ transports: ['websocket', 'polling'] });
|
| 68 |
+
|
| 69 |
+
socket.on('connect', () => {
|
| 70 |
+
$connInd.classList.add('connected');
|
| 71 |
+
updateStatusText();
|
| 72 |
+
});
|
| 73 |
+
|
| 74 |
+
socket.on('disconnect', () => {
|
| 75 |
+
$connInd.classList.remove('connected');
|
| 76 |
+
$statusText.textContent = 'Disconnected';
|
| 77 |
+
});
|
| 78 |
+
|
| 79 |
+
socket.on('connect_error', () => {
|
| 80 |
+
$connInd.classList.remove('connected');
|
| 81 |
+
$statusText.textContent = 'Connection failed';
|
| 82 |
+
});
|
| 83 |
+
|
| 84 |
+
// Initial state from server
|
| 85 |
+
socket.on('bridge:init', (data) => {
|
| 86 |
+
tools = data.tools || [];
|
| 87 |
+
cliInfo = data.cli || {};
|
| 88 |
+
savedSettings = data.settings || {};
|
| 89 |
+
sessionActive = data.sessionActive || false;
|
| 90 |
+
renderToolRail(tools);
|
| 91 |
+
renderIconBar(tools);
|
| 92 |
+
renderChips(tools);
|
| 93 |
+
updateStatusText();
|
| 94 |
+
updateSettingsPanel();
|
| 95 |
+
});
|
| 96 |
+
|
| 97 |
+
// Chat messages
|
| 98 |
+
socket.on('agent:message', (data) => {
|
| 99 |
+
if (data.from === 'system') {
|
| 100 |
+
addMsg('system', '', data.message);
|
| 101 |
+
} else if (data.from !== 'human') {
|
| 102 |
+
addMsg('agent', 'Agent', data.message);
|
| 103 |
+
}
|
| 104 |
+
});
|
| 105 |
+
|
| 106 |
+
// Tool events
|
| 107 |
+
socket.on('tool:started', (data) => {
|
| 108 |
+
addMsg('tool', data.toolName, 'Running...');
|
| 109 |
+
showOutput();
|
| 110 |
+
appendOutput(`[${data.toolName}] Started\n`);
|
| 111 |
+
});
|
| 112 |
+
|
| 113 |
+
socket.on('tool:progress', (data) => {
|
| 114 |
+
appendProgressLine(data.progress);
|
| 115 |
+
});
|
| 116 |
+
|
| 117 |
+
socket.on('tool:completed', (data) => {
|
| 118 |
+
addMsg('tool', data.toolName, 'Completed.');
|
| 119 |
+
renderToolResult(data.result);
|
| 120 |
+
});
|
| 121 |
+
|
| 122 |
+
socket.on('tool:error', (data) => {
|
| 123 |
+
addMsg('error', data.toolName, data.error);
|
| 124 |
+
appendOutput(`[Error] ${data.error}\n`);
|
| 125 |
+
});
|
| 126 |
+
|
| 127 |
+
// CLI stdout/stderr -- these come from the real Antigravity process
|
| 128 |
+
socket.on('cli:stdout', (data) => {
|
| 129 |
+
showOutput();
|
| 130 |
+
appendOutput(data.data);
|
| 131 |
+
// Also show in chat as agent messages (strip ANSI codes)
|
| 132 |
+
const clean = stripAnsi(data.data).trim();
|
| 133 |
+
if (clean) {
|
| 134 |
+
addMsg('agent', 'Antigravity', clean);
|
| 135 |
+
}
|
| 136 |
+
});
|
| 137 |
+
|
| 138 |
+
socket.on('cli:stderr', (data) => {
|
| 139 |
+
showOutput();
|
| 140 |
+
appendOutput(`${data.data}`);
|
| 141 |
+
});
|
| 142 |
+
|
| 143 |
+
socket.on('cli:started', () => {
|
| 144 |
+
sessionActive = true;
|
| 145 |
+
addMsg('system', '', 'Antigravity CLI session started.');
|
| 146 |
+
updateStatusText();
|
| 147 |
+
updateSessionButtons();
|
| 148 |
+
});
|
| 149 |
+
|
| 150 |
+
socket.on('cli:starting', () => {
|
| 151 |
+
addMsg('system', '', 'Starting Antigravity CLI...');
|
| 152 |
+
});
|
| 153 |
+
|
| 154 |
+
socket.on('cli:ended', (data) => {
|
| 155 |
+
sessionActive = false;
|
| 156 |
+
addMsg('system', '', `Antigravity CLI session ended${data.exitCode !== null ? ` (exit code ${data.exitCode})` : ''}.`);
|
| 157 |
+
updateStatusText();
|
| 158 |
+
updateSessionButtons();
|
| 159 |
+
});
|
| 160 |
+
|
| 161 |
+
socket.on('cli:error', (data) => {
|
| 162 |
+
addMsg('error', 'CLI', data.message);
|
| 163 |
+
sessionActive = false;
|
| 164 |
+
updateStatusText();
|
| 165 |
+
updateSessionButtons();
|
| 166 |
+
});
|
| 167 |
+
|
| 168 |
+
// Settings changes from other clients
|
| 169 |
+
socket.on('settings:changed', (data) => {
|
| 170 |
+
savedSettings = data.settings;
|
| 171 |
+
cliInfo = data.cli;
|
| 172 |
+
updateSettingsPanel();
|
| 173 |
+
updateStatusText();
|
| 174 |
+
});
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
// -----------------------------------------------------------------------
|
| 178 |
+
// Status
|
| 179 |
+
// -----------------------------------------------------------------------
|
| 180 |
+
function updateStatusText() {
|
| 181 |
+
const parts = [];
|
| 182 |
+
if (socket?.connected) {
|
| 183 |
+
parts.push('Server: connected');
|
| 184 |
+
} else {
|
| 185 |
+
parts.push('Server: disconnected');
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
if (sessionActive) {
|
| 189 |
+
parts.push('CLI: active session');
|
| 190 |
+
} else if (cliInfo.detected) {
|
| 191 |
+
parts.push('CLI: detected (not running)');
|
| 192 |
+
} else {
|
| 193 |
+
parts.push('CLI: not detected');
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
$statusText.textContent = parts.join(' | ');
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
// -----------------------------------------------------------------------
|
| 200 |
+
// Settings Panel
|
| 201 |
+
// -----------------------------------------------------------------------
|
| 202 |
+
function openSettings() {
|
| 203 |
+
$settingsPanel.classList.remove('hidden');
|
| 204 |
+
$cliPathInput.value = savedSettings.cliPath || '';
|
| 205 |
+
updateSettingsPanel();
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
function closeSettings() {
|
| 209 |
+
$settingsPanel.classList.add('hidden');
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
function updateSettingsPanel() {
|
| 213 |
+
// Detection status
|
| 214 |
+
if (cliInfo.detected) {
|
| 215 |
+
$detectIndicator.className = 'detect-dot found';
|
| 216 |
+
$detectText.textContent = `Detected: ${cliInfo.path || 'unknown'}`;
|
| 217 |
+
$detectDetails.textContent = `Version: ${cliInfo.version || 'unknown'}\nMethod: ${cliInfo.method || 'unknown'}`;
|
| 218 |
+
} else {
|
| 219 |
+
$detectIndicator.className = 'detect-dot missing';
|
| 220 |
+
$detectText.textContent = 'Not detected';
|
| 221 |
+
$detectDetails.textContent = 'Configure the path above and click Save, or install Antigravity CLI and click Re-detect.';
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
// Path input
|
| 225 |
+
if (savedSettings.cliPath && !$cliPathInput.value) {
|
| 226 |
+
$cliPathInput.value = savedSettings.cliPath;
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
updateSessionButtons();
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
function updateSessionButtons() {
|
| 233 |
+
$connectBtn.disabled = sessionActive;
|
| 234 |
+
$disconnectBtn.disabled = !sessionActive;
|
| 235 |
+
$sessionStatus.textContent = sessionActive
|
| 236 |
+
? 'CLI session is active. Messages will be sent to the Antigravity agent.'
|
| 237 |
+
: 'No active session. Click Connect to start.';
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
// Save CLI path
|
| 241 |
+
$savePathBtn.addEventListener('click', () => {
|
| 242 |
+
const path = $cliPathInput.value.trim();
|
| 243 |
+
socket.emit('settings:update', { cliPath: path }, (response) => {
|
| 244 |
+
savedSettings = response.settings;
|
| 245 |
+
cliInfo = response.cli;
|
| 246 |
+
updateSettingsPanel();
|
| 247 |
+
updateStatusText();
|
| 248 |
+
addMsg('system', '', cliInfo.detected
|
| 249 |
+
? `CLI detected at: ${cliInfo.path}`
|
| 250 |
+
: `CLI not found at the specified path. Check the path and try again.`);
|
| 251 |
+
});
|
| 252 |
+
});
|
| 253 |
+
|
| 254 |
+
// Re-detect
|
| 255 |
+
$redetectBtn.addEventListener('click', () => {
|
| 256 |
+
$detectText.textContent = 'Detecting...';
|
| 257 |
+
socket.emit('settings:update', { cliPath: $cliPathInput.value.trim() }, (response) => {
|
| 258 |
+
savedSettings = response.settings;
|
| 259 |
+
cliInfo = response.cli;
|
| 260 |
+
updateSettingsPanel();
|
| 261 |
+
updateStatusText();
|
| 262 |
+
});
|
| 263 |
+
});
|
| 264 |
+
|
| 265 |
+
// Connect CLI session
|
| 266 |
+
$connectBtn.addEventListener('click', () => {
|
| 267 |
+
$connectBtn.disabled = true;
|
| 268 |
+
$sessionStatus.textContent = 'Starting...';
|
| 269 |
+
socket.emit('cli:start', { cliPath: savedSettings.cliPath || cliInfo.path });
|
| 270 |
+
});
|
| 271 |
+
|
| 272 |
+
// Disconnect CLI session
|
| 273 |
+
$disconnectBtn.addEventListener('click', () => {
|
| 274 |
+
socket.emit('cli:stop');
|
| 275 |
+
});
|
| 276 |
+
|
| 277 |
+
$settingsToggle.addEventListener('click', openSettings);
|
| 278 |
+
$settingsClose.addEventListener('click', closeSettings);
|
| 279 |
+
|
| 280 |
+
// Close settings on backdrop click
|
| 281 |
+
$settingsPanel.addEventListener('click', (e) => {
|
| 282 |
+
if (e.target === $settingsPanel) closeSettings();
|
| 283 |
+
});
|
| 284 |
+
|
| 285 |
+
// -----------------------------------------------------------------------
|
| 286 |
+
// Tool Rail
|
| 287 |
+
// -----------------------------------------------------------------------
|
| 288 |
+
function renderToolRail(toolList) {
|
| 289 |
+
$toolRail.innerHTML = '';
|
| 290 |
+
toolList.forEach((tool, i) => {
|
| 291 |
+
const btn = document.createElement('button');
|
| 292 |
+
btn.className = 'rail-btn' + (tool.mock ? '' : ' active');
|
| 293 |
+
btn.textContent = String(i + 1);
|
| 294 |
+
btn.title = tool.name.replace(/_/g, ' ');
|
| 295 |
+
btn.addEventListener('click', () => insertSyntax(tool.syntax || `use <${tool.name}> `));
|
| 296 |
+
$toolRail.appendChild(btn);
|
| 297 |
+
});
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
// -----------------------------------------------------------------------
|
| 301 |
+
// Icon Bar
|
| 302 |
+
// -----------------------------------------------------------------------
|
| 303 |
+
function renderIconBar(toolList) {
|
| 304 |
+
$iconBar.innerHTML = '';
|
| 305 |
+
addIconButton($iconBar, iconSvg('globe'), 'Browse tools', () => openSettings());
|
| 306 |
+
toolList.slice(0, 6).forEach((tool) => {
|
| 307 |
+
addIconButton($iconBar, tool.mock ? iconSvg('box') : iconSvg('zap'),
|
| 308 |
+
tool.name.replace(/_/g, ' '),
|
| 309 |
+
() => insertSyntax(tool.syntax || `use <${tool.name}> `)
|
| 310 |
+
);
|
| 311 |
+
});
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
function addIconButton(parent, svgHtml, title, onClick) {
|
| 315 |
+
const btn = document.createElement('button');
|
| 316 |
+
btn.className = 'icon-btn';
|
| 317 |
+
btn.innerHTML = svgHtml;
|
| 318 |
+
btn.title = title;
|
| 319 |
+
btn.addEventListener('click', onClick);
|
| 320 |
+
parent.appendChild(btn);
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
// -----------------------------------------------------------------------
|
| 324 |
+
// Chips
|
| 325 |
+
// -----------------------------------------------------------------------
|
| 326 |
+
function renderChips(toolList) {
|
| 327 |
+
$chips.innerHTML = '';
|
| 328 |
+
const suggestions = [
|
| 329 |
+
{ label: 'Transcribe YouTube', syntax: 'use <transcript_tool> ' },
|
| 330 |
+
...toolList.filter(t => t.mock).slice(0, 3).map(t => ({
|
| 331 |
+
label: t.name.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()),
|
| 332 |
+
syntax: t.syntax,
|
| 333 |
+
})),
|
| 334 |
+
];
|
| 335 |
+
suggestions.forEach((s) => {
|
| 336 |
+
const chip = document.createElement('button');
|
| 337 |
+
chip.className = 'chip';
|
| 338 |
+
chip.textContent = s.label;
|
| 339 |
+
chip.addEventListener('click', () => insertSyntax(s.syntax));
|
| 340 |
+
$chips.appendChild(chip);
|
| 341 |
+
});
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
// -----------------------------------------------------------------------
|
| 345 |
+
// Input helpers
|
| 346 |
+
// -----------------------------------------------------------------------
|
| 347 |
+
function insertSyntax(syntax) {
|
| 348 |
+
const input = inConversation ? $convInput : $promptInput;
|
| 349 |
+
input.value = syntax;
|
| 350 |
+
input.focus();
|
| 351 |
+
input.setSelectionRange(input.value.length, input.value.length);
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
function send(inputEl) {
|
| 355 |
+
const text = inputEl.value.trim();
|
| 356 |
+
if (!text) return;
|
| 357 |
+
const id = `msg-${++msgCount}-${Date.now()}`;
|
| 358 |
+
if (!inConversation) enterConversation();
|
| 359 |
+
addMsg('user', 'You', text);
|
| 360 |
+
socket.emit('prompt:send', { message: text, id });
|
| 361 |
+
inputEl.value = '';
|
| 362 |
+
inputEl.focus();
|
| 363 |
+
}
|
| 364 |
+
|
| 365 |
+
// -----------------------------------------------------------------------
|
| 366 |
+
// View Switching
|
| 367 |
+
// -----------------------------------------------------------------------
|
| 368 |
+
function enterConversation() {
|
| 369 |
+
inConversation = true;
|
| 370 |
+
$centerCard.style.display = 'none';
|
| 371 |
+
$conversation.classList.remove('hidden');
|
| 372 |
+
$convInput.focus();
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
// -----------------------------------------------------------------------
|
| 376 |
+
// Messages
|
| 377 |
+
// -----------------------------------------------------------------------
|
| 378 |
+
function addMsg(type, label, body) {
|
| 379 |
+
if (!inConversation) enterConversation();
|
| 380 |
+
const div = document.createElement('div');
|
| 381 |
+
if (type === 'system') {
|
| 382 |
+
div.className = 'msg msg-system';
|
| 383 |
+
div.textContent = body;
|
| 384 |
+
} else {
|
| 385 |
+
div.className = 'msg msg-' + type;
|
| 386 |
+
const labelEl = document.createElement('div');
|
| 387 |
+
labelEl.className = 'msg-label';
|
| 388 |
+
labelEl.textContent = label;
|
| 389 |
+
div.appendChild(labelEl);
|
| 390 |
+
const bodyEl = document.createElement('div');
|
| 391 |
+
bodyEl.className = 'msg-body';
|
| 392 |
+
bodyEl.textContent = body;
|
| 393 |
+
div.appendChild(bodyEl);
|
| 394 |
+
}
|
| 395 |
+
$messages.appendChild(div);
|
| 396 |
+
$messages.scrollTop = $messages.scrollHeight;
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
// -----------------------------------------------------------------------
|
| 400 |
+
// Output
|
| 401 |
+
// -----------------------------------------------------------------------
|
| 402 |
+
function showOutput() { $outputArea.classList.remove('hidden'); }
|
| 403 |
+
|
| 404 |
+
function appendOutput(text) {
|
| 405 |
+
const span = document.createElement('span');
|
| 406 |
+
span.textContent = text;
|
| 407 |
+
$outputContent.appendChild(span);
|
| 408 |
+
$outputContent.scrollTop = $outputContent.scrollHeight;
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
function appendProgressLine(text) {
|
| 412 |
+
showOutput();
|
| 413 |
+
const line = document.createElement('div');
|
| 414 |
+
line.className = 'progress-line';
|
| 415 |
+
line.textContent = text;
|
| 416 |
+
$outputContent.appendChild(line);
|
| 417 |
+
$outputContent.scrollTop = $outputContent.scrollHeight;
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
function renderToolResult(result) {
|
| 421 |
+
if (!result) return;
|
| 422 |
+
if (result.transcript) {
|
| 423 |
+
const preview = document.createElement('div');
|
| 424 |
+
preview.className = 'transcript-preview';
|
| 425 |
+
preview.textContent = result.transcript.length > 1500
|
| 426 |
+
? result.transcript.substring(0, 1500) + '\n\n[truncated]'
|
| 427 |
+
: result.transcript;
|
| 428 |
+
$outputContent.appendChild(preview);
|
| 429 |
+
if (result.downloadUrl) {
|
| 430 |
+
const link = document.createElement('a');
|
| 431 |
+
link.className = 'download-link';
|
| 432 |
+
link.href = result.downloadUrl;
|
| 433 |
+
link.download = result.filename || 'transcript.txt';
|
| 434 |
+
link.textContent = 'Download Transcript';
|
| 435 |
+
$outputContent.appendChild(link);
|
| 436 |
+
}
|
| 437 |
+
} else if (result.message) {
|
| 438 |
+
appendOutput('\n' + result.message + '\n');
|
| 439 |
+
}
|
| 440 |
+
$outputContent.scrollTop = $outputContent.scrollHeight;
|
| 441 |
+
}
|
| 442 |
+
|
| 443 |
+
// -----------------------------------------------------------------------
|
| 444 |
+
// Helpers
|
| 445 |
+
// -----------------------------------------------------------------------
|
| 446 |
+
function stripAnsi(str) {
|
| 447 |
+
return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '').replace(/\x1B\][^\x07]*\x07/g, '');
|
| 448 |
+
}
|
| 449 |
+
|
| 450 |
+
function iconSvg(name) {
|
| 451 |
+
const icons = {
|
| 452 |
+
globe: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>',
|
| 453 |
+
zap: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>',
|
| 454 |
+
box: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path><polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline><line x1="12" y1="22.08" x2="12" y2="12"></line></svg>',
|
| 455 |
+
};
|
| 456 |
+
return icons[name] || '';
|
| 457 |
+
}
|
| 458 |
+
|
| 459 |
+
// -----------------------------------------------------------------------
|
| 460 |
+
// Events
|
| 461 |
+
// -----------------------------------------------------------------------
|
| 462 |
+
$promptInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); send($promptInput); } });
|
| 463 |
+
$sendBtn.addEventListener('click', () => send($promptInput));
|
| 464 |
+
$convInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); send($convInput); } });
|
| 465 |
+
$convSendBtn.addEventListener('click', () => send($convInput));
|
| 466 |
+
$cardClose.addEventListener('click', () => { $centerCard.style.display = 'none'; });
|
| 467 |
+
$closeOutput.addEventListener('click', () => { $outputArea.classList.add('hidden'); });
|
| 468 |
+
|
| 469 |
+
$linkTools.addEventListener('click', (e) => {
|
| 470 |
+
e.preventDefault();
|
| 471 |
+
showOutput();
|
| 472 |
+
$outputContent.innerHTML = '';
|
| 473 |
+
appendOutput('Registered Tools:\n');
|
| 474 |
+
tools.forEach((t, i) => {
|
| 475 |
+
appendOutput(` ${i + 1}. ${t.name} ${t.mock ? '[mock]' : '[live]'}\n`);
|
| 476 |
+
appendOutput(` ${t.description}\n`);
|
| 477 |
+
appendOutput(` Syntax: ${t.syntax}\n\n`);
|
| 478 |
+
});
|
| 479 |
+
});
|
| 480 |
+
|
| 481 |
+
$linkBridge.addEventListener('click', (e) => {
|
| 482 |
+
e.preventDefault();
|
| 483 |
+
openSettings();
|
| 484 |
+
});
|
| 485 |
+
|
| 486 |
+
// -----------------------------------------------------------------------
|
| 487 |
+
// Init
|
| 488 |
+
// -----------------------------------------------------------------------
|
| 489 |
+
connect();
|
| 490 |
+
|
| 491 |
+
})();
|
client/src/style.css
ADDED
|
@@ -0,0 +1,888 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* ======================================================================
|
| 2 |
+
Agent Bridge -- Light Theme Design System
|
| 3 |
+
Matching Google Antigravity reference: light canvas, dot-grid,
|
| 4 |
+
centered card, numbered tool rail, suggestion chips.
|
| 5 |
+
Font: DM Sans. No emojis.
|
| 6 |
+
====================================================================== */
|
| 7 |
+
|
| 8 |
+
*,
|
| 9 |
+
*::before,
|
| 10 |
+
*::after {
|
| 11 |
+
margin: 0;
|
| 12 |
+
padding: 0;
|
| 13 |
+
box-sizing: border-box;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
:root {
|
| 17 |
+
--bg: #f8f9fa;
|
| 18 |
+
--bg-card: #ffffff;
|
| 19 |
+
--bg-hover: #f1f3f4;
|
| 20 |
+
--bg-input: #ffffff;
|
| 21 |
+
--bg-chip: #ffffff;
|
| 22 |
+
--bg-output: #f8f9fa;
|
| 23 |
+
|
| 24 |
+
--border: #dadce0;
|
| 25 |
+
--border-light: #e8eaed;
|
| 26 |
+
--border-focus: #4d90fe;
|
| 27 |
+
|
| 28 |
+
--text: #202124;
|
| 29 |
+
--text-secondary: #5f6368;
|
| 30 |
+
--text-muted: #9aa0a6;
|
| 31 |
+
--text-link: #1a73e8;
|
| 32 |
+
|
| 33 |
+
--accent: #1a73e8;
|
| 34 |
+
--accent-light: #e8f0fe;
|
| 35 |
+
--accent-surface: rgba(26, 115, 232, 0.08);
|
| 36 |
+
|
| 37 |
+
--success: #34a853;
|
| 38 |
+
--error: #ea4335;
|
| 39 |
+
--warning: #fbbc04;
|
| 40 |
+
|
| 41 |
+
--font: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
| 42 |
+
--font-mono: 'JetBrains Mono', 'Cascadia Code', 'Consolas', monospace;
|
| 43 |
+
|
| 44 |
+
--radius-sm: 6px;
|
| 45 |
+
--radius-md: 12px;
|
| 46 |
+
--radius-lg: 24px;
|
| 47 |
+
--radius-pill: 100px;
|
| 48 |
+
|
| 49 |
+
--shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
|
| 50 |
+
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
|
| 51 |
+
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.1);
|
| 52 |
+
--shadow-card: 0 1px 6px rgba(0, 0, 0, 0.06), 0 2px 32px rgba(0, 0, 0, 0.04);
|
| 53 |
+
|
| 54 |
+
--grid-color: #e0e0e0;
|
| 55 |
+
--grid-size: 40px;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
html,
|
| 59 |
+
body {
|
| 60 |
+
height: 100%;
|
| 61 |
+
font-family: var(--font);
|
| 62 |
+
font-size: 14px;
|
| 63 |
+
color: var(--text);
|
| 64 |
+
background: var(--bg);
|
| 65 |
+
-webkit-font-smoothing: antialiased;
|
| 66 |
+
overflow: hidden;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
::selection {
|
| 70 |
+
background: var(--accent-light);
|
| 71 |
+
color: var(--accent);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
::-webkit-scrollbar {
|
| 75 |
+
width: 6px;
|
| 76 |
+
height: 6px;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
::-webkit-scrollbar-track {
|
| 80 |
+
background: transparent;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
::-webkit-scrollbar-thumb {
|
| 84 |
+
background: var(--border);
|
| 85 |
+
border-radius: 3px;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
/* --- Layout ---------------------------------------------------------- */
|
| 89 |
+
#app {
|
| 90 |
+
display: flex;
|
| 91 |
+
height: 100vh;
|
| 92 |
+
width: 100vw;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
/* --- Dot Grid Background --------------------------------------------- */
|
| 96 |
+
#canvas {
|
| 97 |
+
flex: 1;
|
| 98 |
+
position: relative;
|
| 99 |
+
display: flex;
|
| 100 |
+
flex-direction: column;
|
| 101 |
+
align-items: center;
|
| 102 |
+
justify-content: center;
|
| 103 |
+
background-color: var(--bg);
|
| 104 |
+
background-image: radial-gradient(circle, var(--grid-color) 1px, transparent 1px);
|
| 105 |
+
background-size: var(--grid-size) var(--grid-size);
|
| 106 |
+
overflow: hidden;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
/* --- Status Bar ------------------------------------------------------ */
|
| 110 |
+
#status-bar {
|
| 111 |
+
position: absolute;
|
| 112 |
+
top: 16px;
|
| 113 |
+
right: 20px;
|
| 114 |
+
display: flex;
|
| 115 |
+
align-items: center;
|
| 116 |
+
gap: 6px;
|
| 117 |
+
font-size: 11px;
|
| 118 |
+
color: var(--text-muted);
|
| 119 |
+
z-index: 10;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
#connection-indicator {
|
| 123 |
+
width: 7px;
|
| 124 |
+
height: 7px;
|
| 125 |
+
border-radius: 50%;
|
| 126 |
+
background: var(--error);
|
| 127 |
+
transition: background 0.3s;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
#connection-indicator.connected {
|
| 131 |
+
background: var(--success);
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
#status-text {
|
| 135 |
+
font-weight: 500;
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
/* --- Tool Rail (Left Numbered Buttons) ------------------------------- */
|
| 139 |
+
#tool-rail {
|
| 140 |
+
display: flex;
|
| 141 |
+
flex-direction: column;
|
| 142 |
+
gap: 8px;
|
| 143 |
+
padding: 16px 10px;
|
| 144 |
+
z-index: 10;
|
| 145 |
+
background: transparent;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
.rail-btn {
|
| 149 |
+
display: flex;
|
| 150 |
+
align-items: center;
|
| 151 |
+
justify-content: center;
|
| 152 |
+
width: 38px;
|
| 153 |
+
height: 38px;
|
| 154 |
+
border: 1.5px solid var(--border);
|
| 155 |
+
border-radius: var(--radius-md);
|
| 156 |
+
background: var(--bg-card);
|
| 157 |
+
color: var(--text-secondary);
|
| 158 |
+
font-family: var(--font);
|
| 159 |
+
font-size: 13px;
|
| 160 |
+
font-weight: 600;
|
| 161 |
+
cursor: pointer;
|
| 162 |
+
transition: all 0.15s ease;
|
| 163 |
+
box-shadow: var(--shadow);
|
| 164 |
+
position: relative;
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
.rail-btn:hover {
|
| 168 |
+
border-color: var(--accent);
|
| 169 |
+
color: var(--accent);
|
| 170 |
+
box-shadow: var(--shadow-md);
|
| 171 |
+
transform: translateY(-1px);
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.rail-btn:active {
|
| 175 |
+
transform: scale(0.95);
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
.rail-btn.active {
|
| 179 |
+
border-color: var(--accent);
|
| 180 |
+
background: var(--accent-light);
|
| 181 |
+
color: var(--accent);
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
.rail-btn[title]::after {
|
| 185 |
+
content: attr(title);
|
| 186 |
+
position: absolute;
|
| 187 |
+
left: calc(100% + 8px);
|
| 188 |
+
top: 50%;
|
| 189 |
+
transform: translateY(-50%);
|
| 190 |
+
background: var(--text);
|
| 191 |
+
color: #fff;
|
| 192 |
+
font-size: 11px;
|
| 193 |
+
font-weight: 500;
|
| 194 |
+
padding: 4px 10px;
|
| 195 |
+
border-radius: var(--radius-sm);
|
| 196 |
+
white-space: nowrap;
|
| 197 |
+
opacity: 0;
|
| 198 |
+
pointer-events: none;
|
| 199 |
+
transition: opacity 0.15s;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
.rail-btn:hover[title]::after {
|
| 203 |
+
opacity: 1;
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
/* --- Center Card ----------------------------------------------------- */
|
| 207 |
+
#center-card {
|
| 208 |
+
position: relative;
|
| 209 |
+
width: 100%;
|
| 210 |
+
max-width: 600px;
|
| 211 |
+
background: var(--bg-card);
|
| 212 |
+
border: 1px solid var(--border-light);
|
| 213 |
+
border-radius: 20px;
|
| 214 |
+
padding: 36px 40px 28px;
|
| 215 |
+
box-shadow: var(--shadow-card);
|
| 216 |
+
animation: cardIn 0.3s ease-out;
|
| 217 |
+
z-index: 5;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
@keyframes cardIn {
|
| 221 |
+
from {
|
| 222 |
+
opacity: 0;
|
| 223 |
+
transform: translateY(12px) scale(0.98);
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
to {
|
| 227 |
+
opacity: 1;
|
| 228 |
+
transform: translateY(0) scale(1);
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
#card-close {
|
| 233 |
+
position: absolute;
|
| 234 |
+
top: 14px;
|
| 235 |
+
right: 18px;
|
| 236 |
+
background: none;
|
| 237 |
+
border: none;
|
| 238 |
+
font-size: 20px;
|
| 239 |
+
color: var(--text-muted);
|
| 240 |
+
cursor: pointer;
|
| 241 |
+
line-height: 1;
|
| 242 |
+
padding: 4px;
|
| 243 |
+
transition: color 0.15s;
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
#card-close:hover {
|
| 247 |
+
color: var(--text);
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
#greeting {
|
| 251 |
+
font-size: 22px;
|
| 252 |
+
font-weight: 600;
|
| 253 |
+
color: var(--text);
|
| 254 |
+
margin-bottom: 20px;
|
| 255 |
+
letter-spacing: -0.4px;
|
| 256 |
+
text-align: center;
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
/* --- Prompt Input ---------------------------------------------------- */
|
| 260 |
+
#prompt-container {
|
| 261 |
+
display: flex;
|
| 262 |
+
align-items: center;
|
| 263 |
+
gap: 8px;
|
| 264 |
+
background: var(--bg-input);
|
| 265 |
+
border: 1.5px solid var(--border);
|
| 266 |
+
border-radius: var(--radius-pill);
|
| 267 |
+
padding: 4px 6px 4px 18px;
|
| 268 |
+
transition: border-color 0.15s, box-shadow 0.15s;
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
#prompt-container:focus-within {
|
| 272 |
+
border-color: var(--border-focus);
|
| 273 |
+
box-shadow: 0 0 0 3px var(--accent-surface);
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
#prompt-input,
|
| 277 |
+
#conv-input {
|
| 278 |
+
flex: 1;
|
| 279 |
+
border: none;
|
| 280 |
+
outline: none;
|
| 281 |
+
background: transparent;
|
| 282 |
+
font-family: var(--font);
|
| 283 |
+
font-size: 14px;
|
| 284 |
+
color: var(--text);
|
| 285 |
+
padding: 10px 0;
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
#prompt-input::placeholder,
|
| 289 |
+
#conv-input::placeholder {
|
| 290 |
+
color: var(--text-muted);
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
#send-btn,
|
| 294 |
+
#conv-send-btn {
|
| 295 |
+
display: flex;
|
| 296 |
+
align-items: center;
|
| 297 |
+
justify-content: center;
|
| 298 |
+
width: 34px;
|
| 299 |
+
height: 34px;
|
| 300 |
+
border: none;
|
| 301 |
+
border-radius: 50%;
|
| 302 |
+
background: var(--accent);
|
| 303 |
+
color: #fff;
|
| 304 |
+
cursor: pointer;
|
| 305 |
+
transition: all 0.15s;
|
| 306 |
+
flex-shrink: 0;
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
#send-btn:hover,
|
| 310 |
+
#conv-send-btn:hover {
|
| 311 |
+
background: #1557b0;
|
| 312 |
+
transform: scale(1.05);
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
/* --- Icon Bar -------------------------------------------------------- */
|
| 316 |
+
#icon-bar {
|
| 317 |
+
display: flex;
|
| 318 |
+
align-items: center;
|
| 319 |
+
gap: 4px;
|
| 320 |
+
margin-top: 12px;
|
| 321 |
+
padding: 0 4px;
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
.icon-btn {
|
| 325 |
+
display: flex;
|
| 326 |
+
align-items: center;
|
| 327 |
+
justify-content: center;
|
| 328 |
+
width: 30px;
|
| 329 |
+
height: 30px;
|
| 330 |
+
border: none;
|
| 331 |
+
border-radius: var(--radius-sm);
|
| 332 |
+
background: transparent;
|
| 333 |
+
color: var(--text-muted);
|
| 334 |
+
cursor: pointer;
|
| 335 |
+
transition: all 0.12s;
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
.icon-btn:hover {
|
| 339 |
+
background: var(--bg-hover);
|
| 340 |
+
color: var(--text-secondary);
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
/* --- Suggestion Chips ------------------------------------------------ */
|
| 344 |
+
#suggestion-chips {
|
| 345 |
+
display: flex;
|
| 346 |
+
flex-wrap: wrap;
|
| 347 |
+
justify-content: center;
|
| 348 |
+
gap: 8px;
|
| 349 |
+
margin-top: 16px;
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
.chip {
|
| 353 |
+
display: inline-flex;
|
| 354 |
+
align-items: center;
|
| 355 |
+
padding: 8px 18px;
|
| 356 |
+
font-family: var(--font);
|
| 357 |
+
font-size: 13px;
|
| 358 |
+
font-weight: 500;
|
| 359 |
+
color: var(--text);
|
| 360 |
+
background: var(--bg-chip);
|
| 361 |
+
border: 1px solid var(--border);
|
| 362 |
+
border-radius: var(--radius-pill);
|
| 363 |
+
cursor: pointer;
|
| 364 |
+
transition: all 0.15s;
|
| 365 |
+
user-select: none;
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
.chip:hover {
|
| 369 |
+
border-color: var(--text-muted);
|
| 370 |
+
box-shadow: var(--shadow);
|
| 371 |
+
transform: translateY(-1px);
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
.chip:active {
|
| 375 |
+
transform: scale(0.97);
|
| 376 |
+
}
|
| 377 |
+
|
| 378 |
+
/* --- Bottom Links ---------------------------------------------------- */
|
| 379 |
+
#bottom-links {
|
| 380 |
+
display: flex;
|
| 381 |
+
justify-content: center;
|
| 382 |
+
gap: 20px;
|
| 383 |
+
margin-top: 20px;
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
.bottom-link {
|
| 387 |
+
display: inline-flex;
|
| 388 |
+
align-items: center;
|
| 389 |
+
gap: 6px;
|
| 390 |
+
font-size: 13px;
|
| 391 |
+
font-weight: 500;
|
| 392 |
+
color: var(--text-secondary);
|
| 393 |
+
text-decoration: none;
|
| 394 |
+
transition: color 0.15s;
|
| 395 |
+
}
|
| 396 |
+
|
| 397 |
+
.bottom-link:hover {
|
| 398 |
+
color: var(--accent);
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
/* --- Conversation View ----------------------------------------------- */
|
| 402 |
+
#conversation {
|
| 403 |
+
display: flex;
|
| 404 |
+
flex-direction: column;
|
| 405 |
+
width: 100%;
|
| 406 |
+
max-width: 780px;
|
| 407 |
+
height: 100%;
|
| 408 |
+
padding: 60px 20px 0;
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
#conversation.hidden {
|
| 412 |
+
display: none;
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
#messages {
|
| 416 |
+
flex: 1;
|
| 417 |
+
overflow-y: auto;
|
| 418 |
+
padding: 20px 0;
|
| 419 |
+
display: flex;
|
| 420 |
+
flex-direction: column;
|
| 421 |
+
gap: 14px;
|
| 422 |
+
}
|
| 423 |
+
|
| 424 |
+
.msg {
|
| 425 |
+
max-width: 85%;
|
| 426 |
+
padding: 12px 16px;
|
| 427 |
+
border-radius: 18px;
|
| 428 |
+
font-size: 14px;
|
| 429 |
+
line-height: 1.55;
|
| 430 |
+
animation: msgIn 0.2s ease-out;
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
@keyframes msgIn {
|
| 434 |
+
from {
|
| 435 |
+
opacity: 0;
|
| 436 |
+
transform: translateY(4px);
|
| 437 |
+
}
|
| 438 |
+
|
| 439 |
+
to {
|
| 440 |
+
opacity: 1;
|
| 441 |
+
transform: translateY(0);
|
| 442 |
+
}
|
| 443 |
+
}
|
| 444 |
+
|
| 445 |
+
.msg-label {
|
| 446 |
+
font-size: 10px;
|
| 447 |
+
font-weight: 600;
|
| 448 |
+
text-transform: uppercase;
|
| 449 |
+
letter-spacing: 0.5px;
|
| 450 |
+
margin-bottom: 4px;
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
.msg-body {
|
| 454 |
+
word-wrap: break-word;
|
| 455 |
+
white-space: pre-wrap;
|
| 456 |
+
}
|
| 457 |
+
|
| 458 |
+
.msg-user {
|
| 459 |
+
align-self: flex-end;
|
| 460 |
+
background: var(--accent);
|
| 461 |
+
color: #fff;
|
| 462 |
+
border-bottom-right-radius: 4px;
|
| 463 |
+
}
|
| 464 |
+
|
| 465 |
+
.msg-user .msg-label {
|
| 466 |
+
color: rgba(255, 255, 255, 0.6);
|
| 467 |
+
}
|
| 468 |
+
|
| 469 |
+
.msg-agent {
|
| 470 |
+
align-self: flex-start;
|
| 471 |
+
background: var(--bg-card);
|
| 472 |
+
border: 1px solid var(--border-light);
|
| 473 |
+
border-bottom-left-radius: 4px;
|
| 474 |
+
}
|
| 475 |
+
|
| 476 |
+
.msg-agent .msg-label {
|
| 477 |
+
color: var(--text-muted);
|
| 478 |
+
}
|
| 479 |
+
|
| 480 |
+
.msg-system {
|
| 481 |
+
align-self: center;
|
| 482 |
+
text-align: center;
|
| 483 |
+
font-size: 12px;
|
| 484 |
+
color: var(--text-muted);
|
| 485 |
+
padding: 6px 14px;
|
| 486 |
+
background: var(--accent-light);
|
| 487 |
+
border-radius: var(--radius-pill);
|
| 488 |
+
}
|
| 489 |
+
|
| 490 |
+
.msg-tool {
|
| 491 |
+
align-self: flex-start;
|
| 492 |
+
background: var(--accent-light);
|
| 493 |
+
border: 1px solid rgba(26, 115, 232, 0.15);
|
| 494 |
+
border-bottom-left-radius: 4px;
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
.msg-tool .msg-label {
|
| 498 |
+
color: var(--accent);
|
| 499 |
+
}
|
| 500 |
+
|
| 501 |
+
.msg-error {
|
| 502 |
+
align-self: flex-start;
|
| 503 |
+
background: #fce8e6;
|
| 504 |
+
border: 1px solid rgba(234, 67, 53, 0.15);
|
| 505 |
+
}
|
| 506 |
+
|
| 507 |
+
.msg-error .msg-label {
|
| 508 |
+
color: var(--error);
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
/* --- Conversation Input ---------------------------------------------- */
|
| 512 |
+
#conv-input-bar {
|
| 513 |
+
display: flex;
|
| 514 |
+
align-items: center;
|
| 515 |
+
gap: 8px;
|
| 516 |
+
padding: 12px 0 20px;
|
| 517 |
+
border-top: 1px solid var(--border-light);
|
| 518 |
+
background: transparent;
|
| 519 |
+
}
|
| 520 |
+
|
| 521 |
+
#conv-input {
|
| 522 |
+
flex: 1;
|
| 523 |
+
padding: 12px 18px;
|
| 524 |
+
border: 1.5px solid var(--border);
|
| 525 |
+
border-radius: var(--radius-pill);
|
| 526 |
+
font-family: var(--font);
|
| 527 |
+
font-size: 14px;
|
| 528 |
+
transition: border-color 0.15s, box-shadow 0.15s;
|
| 529 |
+
}
|
| 530 |
+
|
| 531 |
+
#conv-input:focus {
|
| 532 |
+
border-color: var(--border-focus);
|
| 533 |
+
box-shadow: 0 0 0 3px var(--accent-surface);
|
| 534 |
+
outline: none;
|
| 535 |
+
}
|
| 536 |
+
|
| 537 |
+
/* --- Output Area ----------------------------------------------------- */
|
| 538 |
+
#output-area {
|
| 539 |
+
background: var(--bg-card);
|
| 540 |
+
border: 1px solid var(--border-light);
|
| 541 |
+
border-radius: var(--radius-md);
|
| 542 |
+
margin-bottom: 10px;
|
| 543 |
+
max-height: 300px;
|
| 544 |
+
overflow: hidden;
|
| 545 |
+
display: flex;
|
| 546 |
+
flex-direction: column;
|
| 547 |
+
box-shadow: var(--shadow);
|
| 548 |
+
}
|
| 549 |
+
|
| 550 |
+
#output-area.hidden {
|
| 551 |
+
display: none;
|
| 552 |
+
}
|
| 553 |
+
|
| 554 |
+
#output-header {
|
| 555 |
+
display: flex;
|
| 556 |
+
justify-content: space-between;
|
| 557 |
+
align-items: center;
|
| 558 |
+
padding: 8px 14px;
|
| 559 |
+
border-bottom: 1px solid var(--border-light);
|
| 560 |
+
font-size: 12px;
|
| 561 |
+
font-weight: 600;
|
| 562 |
+
color: var(--text-secondary);
|
| 563 |
+
}
|
| 564 |
+
|
| 565 |
+
#close-output {
|
| 566 |
+
background: none;
|
| 567 |
+
border: none;
|
| 568 |
+
font-size: 16px;
|
| 569 |
+
color: var(--text-muted);
|
| 570 |
+
cursor: pointer;
|
| 571 |
+
}
|
| 572 |
+
|
| 573 |
+
#output-content {
|
| 574 |
+
flex: 1;
|
| 575 |
+
overflow-y: auto;
|
| 576 |
+
padding: 10px 14px;
|
| 577 |
+
font-family: var(--font-mono);
|
| 578 |
+
font-size: 12px;
|
| 579 |
+
line-height: 1.6;
|
| 580 |
+
color: var(--text-secondary);
|
| 581 |
+
white-space: pre-wrap;
|
| 582 |
+
}
|
| 583 |
+
|
| 584 |
+
.progress-line {
|
| 585 |
+
color: var(--text-muted);
|
| 586 |
+
padding: 1px 0;
|
| 587 |
+
}
|
| 588 |
+
|
| 589 |
+
.progress-line::before {
|
| 590 |
+
content: '> ';
|
| 591 |
+
color: var(--accent);
|
| 592 |
+
}
|
| 593 |
+
|
| 594 |
+
.transcript-preview {
|
| 595 |
+
max-height: 200px;
|
| 596 |
+
overflow-y: auto;
|
| 597 |
+
padding: 8px;
|
| 598 |
+
background: var(--bg-output);
|
| 599 |
+
border-radius: var(--radius-sm);
|
| 600 |
+
margin-top: 6px;
|
| 601 |
+
border: 1px solid var(--border-light);
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
.download-link {
|
| 605 |
+
display: inline-flex;
|
| 606 |
+
align-items: center;
|
| 607 |
+
gap: 6px;
|
| 608 |
+
margin-top: 8px;
|
| 609 |
+
padding: 6px 14px;
|
| 610 |
+
font-family: var(--font);
|
| 611 |
+
font-size: 12px;
|
| 612 |
+
font-weight: 600;
|
| 613 |
+
color: #fff;
|
| 614 |
+
background: var(--accent);
|
| 615 |
+
border: none;
|
| 616 |
+
border-radius: var(--radius-sm);
|
| 617 |
+
cursor: pointer;
|
| 618 |
+
text-decoration: none;
|
| 619 |
+
transition: background 0.15s;
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
.download-link:hover {
|
| 623 |
+
background: #1557b0;
|
| 624 |
+
}
|
| 625 |
+
|
| 626 |
+
/* --- Spinner --------------------------------------------------------- */
|
| 627 |
+
.spinner {
|
| 628 |
+
display: inline-block;
|
| 629 |
+
width: 12px;
|
| 630 |
+
height: 12px;
|
| 631 |
+
border: 2px solid var(--border);
|
| 632 |
+
border-top-color: var(--accent);
|
| 633 |
+
border-radius: 50%;
|
| 634 |
+
animation: spin 0.6s linear infinite;
|
| 635 |
+
margin-right: 4px;
|
| 636 |
+
vertical-align: middle;
|
| 637 |
+
}
|
| 638 |
+
|
| 639 |
+
@keyframes spin {
|
| 640 |
+
to {
|
| 641 |
+
transform: rotate(360deg);
|
| 642 |
+
}
|
| 643 |
+
}
|
| 644 |
+
|
| 645 |
+
/* --- Settings Panel -------------------------------------------------- */
|
| 646 |
+
.settings-btn {
|
| 647 |
+
display: flex;
|
| 648 |
+
align-items: center;
|
| 649 |
+
justify-content: center;
|
| 650 |
+
width: 30px;
|
| 651 |
+
height: 30px;
|
| 652 |
+
border: none;
|
| 653 |
+
border-radius: var(--radius-sm);
|
| 654 |
+
background: transparent;
|
| 655 |
+
color: var(--text-muted);
|
| 656 |
+
cursor: pointer;
|
| 657 |
+
transition: all 0.12s;
|
| 658 |
+
margin-left: 4px;
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
.settings-btn:hover {
|
| 662 |
+
background: var(--bg-hover);
|
| 663 |
+
color: var(--text-secondary);
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
+
#settings-panel {
|
| 667 |
+
position: absolute;
|
| 668 |
+
inset: 0;
|
| 669 |
+
z-index: 50;
|
| 670 |
+
display: flex;
|
| 671 |
+
align-items: center;
|
| 672 |
+
justify-content: center;
|
| 673 |
+
background: rgba(0, 0, 0, 0.15);
|
| 674 |
+
backdrop-filter: blur(2px);
|
| 675 |
+
animation: fadeIn 0.15s ease-out;
|
| 676 |
+
}
|
| 677 |
+
|
| 678 |
+
#settings-panel.hidden {
|
| 679 |
+
display: none;
|
| 680 |
+
}
|
| 681 |
+
|
| 682 |
+
@keyframes fadeIn {
|
| 683 |
+
from {
|
| 684 |
+
opacity: 0;
|
| 685 |
+
}
|
| 686 |
+
|
| 687 |
+
to {
|
| 688 |
+
opacity: 1;
|
| 689 |
+
}
|
| 690 |
+
}
|
| 691 |
+
|
| 692 |
+
.settings-card {
|
| 693 |
+
width: 100%;
|
| 694 |
+
max-width: 480px;
|
| 695 |
+
background: var(--bg-card);
|
| 696 |
+
border: 1px solid var(--border-light);
|
| 697 |
+
border-radius: 16px;
|
| 698 |
+
box-shadow: var(--shadow-lg);
|
| 699 |
+
overflow: hidden;
|
| 700 |
+
}
|
| 701 |
+
|
| 702 |
+
.settings-header {
|
| 703 |
+
display: flex;
|
| 704 |
+
align-items: center;
|
| 705 |
+
justify-content: space-between;
|
| 706 |
+
padding: 16px 20px;
|
| 707 |
+
border-bottom: 1px solid var(--border-light);
|
| 708 |
+
}
|
| 709 |
+
|
| 710 |
+
.settings-header h2 {
|
| 711 |
+
font-size: 15px;
|
| 712 |
+
font-weight: 600;
|
| 713 |
+
}
|
| 714 |
+
|
| 715 |
+
.settings-header button {
|
| 716 |
+
background: none;
|
| 717 |
+
border: none;
|
| 718 |
+
font-size: 20px;
|
| 719 |
+
color: var(--text-muted);
|
| 720 |
+
cursor: pointer;
|
| 721 |
+
}
|
| 722 |
+
|
| 723 |
+
.settings-header button:hover {
|
| 724 |
+
color: var(--text);
|
| 725 |
+
}
|
| 726 |
+
|
| 727 |
+
.settings-body {
|
| 728 |
+
padding: 20px;
|
| 729 |
+
}
|
| 730 |
+
|
| 731 |
+
.settings-label {
|
| 732 |
+
display: block;
|
| 733 |
+
font-size: 12px;
|
| 734 |
+
font-weight: 600;
|
| 735 |
+
color: var(--text-secondary);
|
| 736 |
+
margin-bottom: 6px;
|
| 737 |
+
}
|
| 738 |
+
|
| 739 |
+
.settings-input-row {
|
| 740 |
+
display: flex;
|
| 741 |
+
gap: 8px;
|
| 742 |
+
}
|
| 743 |
+
|
| 744 |
+
.settings-input-row input {
|
| 745 |
+
flex: 1;
|
| 746 |
+
padding: 9px 14px;
|
| 747 |
+
border: 1.5px solid var(--border);
|
| 748 |
+
border-radius: var(--radius-sm);
|
| 749 |
+
font-family: var(--font-mono);
|
| 750 |
+
font-size: 12px;
|
| 751 |
+
color: var(--text);
|
| 752 |
+
background: var(--bg);
|
| 753 |
+
outline: none;
|
| 754 |
+
transition: border-color 0.15s;
|
| 755 |
+
}
|
| 756 |
+
|
| 757 |
+
.settings-input-row input:focus {
|
| 758 |
+
border-color: var(--accent);
|
| 759 |
+
}
|
| 760 |
+
|
| 761 |
+
.settings-hint {
|
| 762 |
+
font-size: 11px;
|
| 763 |
+
color: var(--text-muted);
|
| 764 |
+
margin-top: 4px;
|
| 765 |
+
}
|
| 766 |
+
|
| 767 |
+
.settings-divider {
|
| 768 |
+
height: 1px;
|
| 769 |
+
background: var(--border-light);
|
| 770 |
+
margin: 16px 0;
|
| 771 |
+
}
|
| 772 |
+
|
| 773 |
+
.detection-box {
|
| 774 |
+
display: flex;
|
| 775 |
+
align-items: center;
|
| 776 |
+
gap: 8px;
|
| 777 |
+
padding: 10px 14px;
|
| 778 |
+
background: var(--bg);
|
| 779 |
+
border: 1px solid var(--border-light);
|
| 780 |
+
border-radius: var(--radius-sm);
|
| 781 |
+
font-size: 13px;
|
| 782 |
+
}
|
| 783 |
+
|
| 784 |
+
.detect-dot {
|
| 785 |
+
width: 8px;
|
| 786 |
+
height: 8px;
|
| 787 |
+
border-radius: 50%;
|
| 788 |
+
background: var(--text-muted);
|
| 789 |
+
flex-shrink: 0;
|
| 790 |
+
}
|
| 791 |
+
|
| 792 |
+
.detect-dot.found {
|
| 793 |
+
background: var(--success);
|
| 794 |
+
}
|
| 795 |
+
|
| 796 |
+
.detect-dot.missing {
|
| 797 |
+
background: var(--error);
|
| 798 |
+
}
|
| 799 |
+
|
| 800 |
+
.detect-details {
|
| 801 |
+
font-family: var(--font-mono);
|
| 802 |
+
font-size: 11px;
|
| 803 |
+
color: var(--text-muted);
|
| 804 |
+
margin-top: 6px;
|
| 805 |
+
line-height: 1.5;
|
| 806 |
+
}
|
| 807 |
+
|
| 808 |
+
.settings-actions {
|
| 809 |
+
display: flex;
|
| 810 |
+
gap: 8px;
|
| 811 |
+
margin-top: 4px;
|
| 812 |
+
}
|
| 813 |
+
|
| 814 |
+
.btn-primary {
|
| 815 |
+
padding: 8px 18px;
|
| 816 |
+
font-family: var(--font);
|
| 817 |
+
font-size: 13px;
|
| 818 |
+
font-weight: 600;
|
| 819 |
+
color: #fff;
|
| 820 |
+
background: var(--accent);
|
| 821 |
+
border: none;
|
| 822 |
+
border-radius: var(--radius-sm);
|
| 823 |
+
cursor: pointer;
|
| 824 |
+
transition: background 0.15s;
|
| 825 |
+
}
|
| 826 |
+
|
| 827 |
+
.btn-primary:hover {
|
| 828 |
+
background: #1557b0;
|
| 829 |
+
}
|
| 830 |
+
|
| 831 |
+
.btn-primary:disabled {
|
| 832 |
+
opacity: 0.5;
|
| 833 |
+
cursor: not-allowed;
|
| 834 |
+
}
|
| 835 |
+
|
| 836 |
+
.btn-outline {
|
| 837 |
+
padding: 8px 18px;
|
| 838 |
+
font-family: var(--font);
|
| 839 |
+
font-size: 13px;
|
| 840 |
+
font-weight: 500;
|
| 841 |
+
color: var(--text-secondary);
|
| 842 |
+
background: var(--bg-card);
|
| 843 |
+
border: 1px solid var(--border);
|
| 844 |
+
border-radius: var(--radius-sm);
|
| 845 |
+
cursor: pointer;
|
| 846 |
+
transition: all 0.15s;
|
| 847 |
+
}
|
| 848 |
+
|
| 849 |
+
.btn-outline:hover {
|
| 850 |
+
border-color: var(--text-muted);
|
| 851 |
+
}
|
| 852 |
+
|
| 853 |
+
.btn-outline:disabled {
|
| 854 |
+
opacity: 0.5;
|
| 855 |
+
cursor: not-allowed;
|
| 856 |
+
}
|
| 857 |
+
|
| 858 |
+
.btn-danger {
|
| 859 |
+
color: var(--error);
|
| 860 |
+
border-color: rgba(234, 67, 53, 0.3);
|
| 861 |
+
}
|
| 862 |
+
|
| 863 |
+
.btn-danger:hover {
|
| 864 |
+
border-color: var(--error);
|
| 865 |
+
background: #fce8e6;
|
| 866 |
+
}
|
| 867 |
+
|
| 868 |
+
/* --- Responsive ------------------------------------------------------ */
|
| 869 |
+
@media (max-width: 640px) {
|
| 870 |
+
#center-card {
|
| 871 |
+
margin: 0 12px;
|
| 872 |
+
padding: 28px 20px 22px;
|
| 873 |
+
}
|
| 874 |
+
|
| 875 |
+
#greeting {
|
| 876 |
+
font-size: 18px;
|
| 877 |
+
}
|
| 878 |
+
|
| 879 |
+
#tool-rail {
|
| 880 |
+
padding: 12px 6px;
|
| 881 |
+
}
|
| 882 |
+
|
| 883 |
+
.rail-btn {
|
| 884 |
+
width: 32px;
|
| 885 |
+
height: 32px;
|
| 886 |
+
font-size: 11px;
|
| 887 |
+
}
|
| 888 |
+
}
|
output/.gitkeep
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
This directory contains files generated by tools (transcripts, exports, etc.).
|
| 2 |
+
It is excluded from version control via .gitignore.
|
output/Q-TpLKXDEEo-transcript.txt
ADDED
|
File without changes
|
output/X-Dwe-s9URU-transcript.txt
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Google's Android is the best AI
|
| 2 |
+
assistant coding IDE in 2026.
|
| 3 |
+
On the other hand, Open Code is open
|
| 4 |
+
source and it can generate code almost
|
| 5 |
+
as good as cloud code or Gemini. But
|
| 6 |
+
open code is completely free and there
|
| 7 |
+
is no limit of how much you can use open
|
| 8 |
+
code. So if you just combine
|
| 9 |
+
anti-gravity and open code, you are
|
| 10 |
+
going to get a free AI tool which will
|
| 11 |
+
let you build any kind of website
|
| 12 |
+
completely for free. So in this tutorial
|
| 13 |
+
I'm going to show you how you can use
|
| 14 |
+
anti-gabit with open code and create a
|
| 15 |
+
complete project both front end and back
|
| 16 |
+
end. So for it uh first of all open your
|
| 17 |
+
Android. So right now we are going to
|
| 18 |
+
create a folder and we're going to open
|
| 19 |
+
that folder in the and gravity. So right
|
| 20 |
+
now I'm going to click on file then
|
| 21 |
+
click here open folder and let me just
|
| 22 |
+
create a folder over here. So let me
|
| 23 |
+
just right click then click on new and
|
| 24 |
+
the folder name will be let's say
|
| 25 |
+
anti-gravity
|
| 26 |
+
dash open code then I'm going to select
|
| 27 |
+
this one then click here select folder
|
| 28 |
+
and there you can see this folder is
|
| 29 |
+
opened over here. So right now we're
|
| 30 |
+
going to install open code in the antiD.
|
| 31 |
+
So how can we do it? So for it here you
|
| 32 |
+
can see extensions option here. So you
|
| 33 |
+
need to click here and in the search
|
| 34 |
+
option you need to search for open code.
|
| 35 |
+
So once you search for open code you
|
| 36 |
+
will see this icon over here. Here you
|
| 37 |
+
can see open code. So we need to click
|
| 38 |
+
here and there you can see install
|
| 39 |
+
option. So you need to click on install
|
| 40 |
+
and there you can see it is installed.
|
| 41 |
+
So after that in order to open the open
|
| 42 |
+
code CLI here at the top you will see
|
| 43 |
+
this icon over here. The thing is if you
|
| 44 |
+
now just close this you won't be able to
|
| 45 |
+
see that icon. So you need to go to the
|
| 46 |
+
extension uh search for open code then
|
| 47 |
+
click here and then you'll be able to
|
| 48 |
+
see the icon here. So you need to click
|
| 49 |
+
on it and it will open the open code CLI
|
| 50 |
+
over here. So there you can see. So now
|
| 51 |
+
I can just close this and let me just go
|
| 52 |
+
to my folder location.
|
| 53 |
+
So first of all what we are going to do
|
| 54 |
+
is that we're going to generate a plan
|
| 55 |
+
using anti-gravity and we are going to
|
| 56 |
+
execute that plan using open code. But
|
| 57 |
+
before that here you can see
|
| 58 |
+
anti-gravity settings. So here you need
|
| 59 |
+
to click on anti-gravity setting and
|
| 60 |
+
here uh since we are using anti-gravity
|
| 61 |
+
with open code. So for this just make a
|
| 62 |
+
little bit of changes and that is here
|
| 63 |
+
you can see auto execution. Here you can
|
| 64 |
+
see you need to make it uh request
|
| 65 |
+
review and then choose this request
|
| 66 |
+
review as well and just uh do this when
|
| 67 |
+
you are using anti-gravity with open
|
| 68 |
+
code. Okay. So after that just minimize
|
| 69 |
+
that and then I'm just going to write a
|
| 70 |
+
prompt over here and that prompt is I
|
| 71 |
+
want to build a inventory management
|
| 72 |
+
system using Django and SQLite. I want
|
| 73 |
+
the admin panel design to be modern and
|
| 74 |
+
with fully functional. Do not use Django
|
| 75 |
+
uh default admin panel. So I want to
|
| 76 |
+
make a plan for this. So I'm going to
|
| 77 |
+
select the planning mode. Then for the
|
| 78 |
+
thinking uh we are going to choose this
|
| 79 |
+
model cloud opas 4.5 thinking. So this
|
| 80 |
+
this is really good at generating plan.
|
| 81 |
+
So that's why I choose this model. Then
|
| 82 |
+
I'm going to click here send and it will
|
| 83 |
+
start creating a plan for us. And there
|
| 84 |
+
you can see it created a tax file for us
|
| 85 |
+
and it also created the implementation
|
| 86 |
+
plan for us. And uh since we made those
|
| 87 |
+
two autogation off that's why it just
|
| 88 |
+
stop after making the implementation
|
| 89 |
+
plan. So right now here uh you can just
|
| 90 |
+
give the implementation plan. So right
|
| 91 |
+
now I'll just directly copy the plan
|
| 92 |
+
from here
|
| 93 |
+
and I'll just copy it up until this
|
| 94 |
+
part. So I'll just copy it. Ctrl C. So
|
| 95 |
+
after copying the plan I'm just going to
|
| 96 |
+
close those and I'm going to paste the
|
| 97 |
+
prompt in the open code. And one more
|
| 98 |
+
thing that is if I just over here
|
| 99 |
+
/models then press enter it will show us
|
| 100 |
+
all the available model over here. You
|
| 101 |
+
can see and if you want to see all the
|
| 102 |
+
providers you need to press Ctrl + A but
|
| 103 |
+
we are going to choose this model Bigple
|
| 104 |
+
which is completely free and there is no
|
| 105 |
+
limit of how much you can use. So I'm
|
| 106 |
+
just going to uh choose big pickle and
|
| 107 |
+
here I'm going to paste the prompt. So
|
| 108 |
+
I'll just press Ctrl E and there you can
|
| 109 |
+
see pasted 120 lines.
|
| 110 |
+
So we are going to press enter and open
|
| 111 |
+
code will start executing the plan for
|
| 112 |
+
us. So this will take some time. Right
|
| 113 |
+
now I'll just fast forward the video and
|
| 114 |
+
this will uh create both font and
|
| 115 |
+
backend part for us. Okay. So here you
|
| 116 |
+
can see the prompt is complete and it
|
| 117 |
+
took around 18 minute 21 second and here
|
| 118 |
+
you can see it is uh telling us to run
|
| 119 |
+
the project. So it actually
|
| 120 |
+
automatically uh run those command but
|
| 121 |
+
if you want you can run those command
|
| 122 |
+
again but the only command that we need
|
| 123 |
+
to run is this one python manage.py run
|
| 124 |
+
server. So for it we need to open the
|
| 125 |
+
command prompts and uh here you can see
|
| 126 |
+
terminal. So I'm going to click on
|
| 127 |
+
terminal then click here new terminal
|
| 128 |
+
and here we are going to say we are
|
| 129 |
+
going to write this command python
|
| 130 |
+
manage.py run server. So I'll just copy
|
| 131 |
+
and paste it over here then press enter.
|
| 132 |
+
So this will run our project. So here
|
| 133 |
+
you can see it give us a address. So I'm
|
| 134 |
+
just going to copy this address. Then
|
| 135 |
+
I'll go to any browser then paste it
|
| 136 |
+
here then press enter and there you can
|
| 137 |
+
see this is our login interface. So
|
| 138 |
+
first of all we need to give the
|
| 139 |
+
username and password. So if you face an
|
| 140 |
+
error just copy the error and paste it
|
| 141 |
+
over here and open code will solve the
|
| 142 |
+
issue for you. So here we're going to
|
| 143 |
+
ask for the admin username and password.
|
| 144 |
+
So I'm going to say over here give me
|
| 145 |
+
admin username
|
| 146 |
+
and password then press enter. So it
|
| 147 |
+
will give us the admin username and
|
| 148 |
+
password and you can see this is the
|
| 149 |
+
username. Username is admin password is
|
| 150 |
+
admin 1 2 3. So I'm going to s here
|
| 151 |
+
admin
|
| 152 |
+
and password will be admin 1 2 3. Then
|
| 153 |
+
click here sign in.
|
| 154 |
+
And there you can see it took us to the
|
| 155 |
+
admin dashboard and the design is
|
| 156 |
+
actually really solid as you can see. So
|
| 157 |
+
the planning was created by anti gravity
|
| 158 |
+
and designed us or the code was written
|
| 159 |
+
by open code and the design is actually
|
| 160 |
+
really good. There you can see there is
|
| 161 |
+
some hover effect over here and the
|
| 162 |
+
color combination is awesome
|
| 163 |
+
and there is no error over here. And one
|
| 164 |
+
more thing if there is any error you can
|
| 165 |
+
just copy the error and give it over
|
| 166 |
+
here. And if for some reason uh let's
|
| 167 |
+
say you do not like the design by open
|
| 168 |
+
code then you can use anti to make the
|
| 169 |
+
design. As you can see uh we made the
|
| 170 |
+
complete font and back end all the
|
| 171 |
+
database everything with just a simple
|
| 172 |
+
single command. We just keep the command
|
| 173 |
+
in the antigavi antigavity make the plan
|
| 174 |
+
and open code write the code for us and
|
| 175 |
+
it give us a awesome website or
|
| 176 |
+
inventory system which looks premium and
|
| 177 |
+
everything working properly as you can
|
| 178 |
+
see. So yeah I hope you right now know
|
| 179 |
+
how you can use anti with open code and
|
| 180 |
+
create any kind of project completely
|
| 181 |
+
for free. I hope this was useful. If you
|
| 182 |
+
want me to make any kind of specific
|
| 183 |
+
video related to AI, just let me know in
|
| 184 |
+
the comment section.
|
output/X-Dwe-s9URU.en.vtt
ADDED
|
@@ -0,0 +1,1472 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
WEBVTT
|
| 2 |
+
Kind: captions
|
| 3 |
+
Language: en
|
| 4 |
+
|
| 5 |
+
00:00:00.160 --> 00:00:02.950 align:start position:0%
|
| 6 |
+
|
| 7 |
+
Google's<00:00:00.719><c> Android</c><00:00:01.600><c> is</c><00:00:01.839><c> the</c><00:00:02.159><c> best</c><00:00:02.560><c> AI</c>
|
| 8 |
+
|
| 9 |
+
00:00:02.950 --> 00:00:02.960 align:start position:0%
|
| 10 |
+
Google's Android is the best AI
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
00:00:02.960 --> 00:00:06.789 align:start position:0%
|
| 14 |
+
Google's Android is the best AI
|
| 15 |
+
assistant<00:00:03.600><c> coding</c><00:00:04.160><c> IDE</c><00:00:04.799><c> in</c><00:00:05.279><c> 2026.</c>
|
| 16 |
+
|
| 17 |
+
00:00:06.789 --> 00:00:06.799 align:start position:0%
|
| 18 |
+
assistant coding IDE in 2026.
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
00:00:06.799 --> 00:00:09.030 align:start position:0%
|
| 22 |
+
assistant coding IDE in 2026.
|
| 23 |
+
On<00:00:06.960><c> the</c><00:00:07.120><c> other</c><00:00:07.279><c> hand,</c><00:00:07.600><c> Open</c><00:00:08.000><c> Code</c><00:00:08.320><c> is</c><00:00:08.720><c> open</c>
|
| 24 |
+
|
| 25 |
+
00:00:09.030 --> 00:00:09.040 align:start position:0%
|
| 26 |
+
On the other hand, Open Code is open
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
00:00:09.040 --> 00:00:12.070 align:start position:0%
|
| 30 |
+
On the other hand, Open Code is open
|
| 31 |
+
source<00:00:09.679><c> and</c><00:00:10.000><c> it</c><00:00:10.240><c> can</c><00:00:10.400><c> generate</c><00:00:11.120><c> code</c><00:00:11.679><c> almost</c>
|
| 32 |
+
|
| 33 |
+
00:00:12.070 --> 00:00:12.080 align:start position:0%
|
| 34 |
+
source and it can generate code almost
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
00:00:12.080 --> 00:00:15.030 align:start position:0%
|
| 38 |
+
source and it can generate code almost
|
| 39 |
+
as<00:00:12.400><c> good</c><00:00:12.559><c> as</c><00:00:13.040><c> cloud</c><00:00:13.360><c> code</c><00:00:13.679><c> or</c><00:00:13.920><c> Gemini.</c><00:00:14.719><c> But</c>
|
| 40 |
+
|
| 41 |
+
00:00:15.030 --> 00:00:15.040 align:start position:0%
|
| 42 |
+
as good as cloud code or Gemini. But
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
00:00:15.040 --> 00:00:17.109 align:start position:0%
|
| 46 |
+
as good as cloud code or Gemini. But
|
| 47 |
+
open<00:00:15.360><c> code</c><00:00:15.599><c> is</c><00:00:15.839><c> completely</c><00:00:16.400><c> free</c><00:00:16.640><c> and</c><00:00:16.880><c> there</c>
|
| 48 |
+
|
| 49 |
+
00:00:17.109 --> 00:00:17.119 align:start position:0%
|
| 50 |
+
open code is completely free and there
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
00:00:17.119 --> 00:00:20.230 align:start position:0%
|
| 54 |
+
open code is completely free and there
|
| 55 |
+
is<00:00:17.440><c> no</c><00:00:17.680><c> limit</c><00:00:18.240><c> of</c><00:00:18.640><c> how</c><00:00:18.960><c> much</c><00:00:19.199><c> you</c><00:00:19.439><c> can</c><00:00:19.600><c> use</c><00:00:20.000><c> open</c>
|
| 56 |
+
|
| 57 |
+
00:00:20.230 --> 00:00:20.240 align:start position:0%
|
| 58 |
+
is no limit of how much you can use open
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
00:00:20.240 --> 00:00:21.910 align:start position:0%
|
| 62 |
+
is no limit of how much you can use open
|
| 63 |
+
code.<00:00:20.640><c> So</c><00:00:20.880><c> if</c><00:00:21.039><c> you</c><00:00:21.199><c> just</c><00:00:21.439><c> combine</c>
|
| 64 |
+
|
| 65 |
+
00:00:21.910 --> 00:00:21.920 align:start position:0%
|
| 66 |
+
code. So if you just combine
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
00:00:21.920 --> 00:00:23.990 align:start position:0%
|
| 70 |
+
code. So if you just combine
|
| 71 |
+
anti-gravity<00:00:22.640><c> and</c><00:00:22.880><c> open</c><00:00:23.119><c> code,</c><00:00:23.519><c> you</c><00:00:23.760><c> are</c>
|
| 72 |
+
|
| 73 |
+
00:00:23.990 --> 00:00:24.000 align:start position:0%
|
| 74 |
+
anti-gravity and open code, you are
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
00:00:24.000 --> 00:00:27.189 align:start position:0%
|
| 78 |
+
anti-gravity and open code, you are
|
| 79 |
+
going<00:00:24.320><c> to</c><00:00:24.560><c> get</c><00:00:24.800><c> a</c><00:00:25.119><c> free</c><00:00:25.519><c> AI</c><00:00:26.000><c> tool</c><00:00:26.640><c> which</c><00:00:26.960><c> will</c>
|
| 80 |
+
|
| 81 |
+
00:00:27.189 --> 00:00:27.199 align:start position:0%
|
| 82 |
+
going to get a free AI tool which will
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
00:00:27.199 --> 00:00:29.349 align:start position:0%
|
| 86 |
+
going to get a free AI tool which will
|
| 87 |
+
let<00:00:27.439><c> you</c><00:00:27.680><c> build</c><00:00:28.080><c> any</c><00:00:28.320><c> kind</c><00:00:28.560><c> of</c><00:00:28.800><c> website</c>
|
| 88 |
+
|
| 89 |
+
00:00:29.349 --> 00:00:29.359 align:start position:0%
|
| 90 |
+
let you build any kind of website
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
00:00:29.359 --> 00:00:31.669 align:start position:0%
|
| 94 |
+
let you build any kind of website
|
| 95 |
+
completely<00:00:29.920><c> for</c><00:00:30.160><c> free.</c><00:00:30.720><c> So</c><00:00:30.880><c> in</c><00:00:31.119><c> this</c><00:00:31.279><c> tutorial</c>
|
| 96 |
+
|
| 97 |
+
00:00:31.669 --> 00:00:31.679 align:start position:0%
|
| 98 |
+
completely for free. So in this tutorial
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
00:00:31.679 --> 00:00:33.350 align:start position:0%
|
| 102 |
+
completely for free. So in this tutorial
|
| 103 |
+
I'm<00:00:32.000><c> going</c><00:00:32.079><c> to</c><00:00:32.160><c> show</c><00:00:32.320><c> you</c><00:00:32.480><c> how</c><00:00:32.640><c> you</c><00:00:32.800><c> can</c><00:00:33.040><c> use</c>
|
| 104 |
+
|
| 105 |
+
00:00:33.350 --> 00:00:33.360 align:start position:0%
|
| 106 |
+
I'm going to show you how you can use
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
00:00:33.360 --> 00:00:36.229 align:start position:0%
|
| 110 |
+
I'm going to show you how you can use
|
| 111 |
+
anti-gabit<00:00:34.480><c> with</c><00:00:34.800><c> open</c><00:00:35.200><c> code</c><00:00:35.440><c> and</c><00:00:35.760><c> create</c><00:00:35.920><c> a</c>
|
| 112 |
+
|
| 113 |
+
00:00:36.229 --> 00:00:36.239 align:start position:0%
|
| 114 |
+
anti-gabit with open code and create a
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
00:00:36.239 --> 00:00:38.630 align:start position:0%
|
| 118 |
+
anti-gabit with open code and create a
|
| 119 |
+
complete<00:00:36.719><c> project</c><00:00:37.120><c> both</c><00:00:37.440><c> front</c><00:00:37.680><c> end</c><00:00:38.000><c> and</c><00:00:38.399><c> back</c>
|
| 120 |
+
|
| 121 |
+
00:00:38.630 --> 00:00:38.640 align:start position:0%
|
| 122 |
+
complete project both front end and back
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
00:00:38.640 --> 00:00:41.030 align:start position:0%
|
| 126 |
+
complete project both front end and back
|
| 127 |
+
end.<00:00:38.960><c> So</c><00:00:39.120><c> for</c><00:00:39.360><c> it</c><00:00:39.760><c> uh</c><00:00:39.920><c> first</c><00:00:40.079><c> of</c><00:00:40.160><c> all</c><00:00:40.399><c> open</c><00:00:40.640><c> your</c>
|
| 128 |
+
|
| 129 |
+
00:00:41.030 --> 00:00:41.040 align:start position:0%
|
| 130 |
+
end. So for it uh first of all open your
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
00:00:41.040 --> 00:00:43.750 align:start position:0%
|
| 134 |
+
end. So for it uh first of all open your
|
| 135 |
+
Android.<00:00:42.239><c> So</c><00:00:42.399><c> right</c><00:00:42.640><c> now</c><00:00:42.800><c> we</c><00:00:43.040><c> are</c><00:00:43.120><c> going</c><00:00:43.280><c> to</c>
|
| 136 |
+
|
| 137 |
+
00:00:43.750 --> 00:00:43.760 align:start position:0%
|
| 138 |
+
Android. So right now we are going to
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
00:00:43.760 --> 00:00:45.830 align:start position:0%
|
| 142 |
+
Android. So right now we are going to
|
| 143 |
+
create<00:00:44.000><c> a</c><00:00:44.320><c> folder</c><00:00:44.879><c> and</c><00:00:45.120><c> we're</c><00:00:45.360><c> going</c><00:00:45.520><c> to</c><00:00:45.600><c> open</c>
|
| 144 |
+
|
| 145 |
+
00:00:45.830 --> 00:00:45.840 align:start position:0%
|
| 146 |
+
create a folder and we're going to open
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
00:00:45.840 --> 00:00:47.990 align:start position:0%
|
| 150 |
+
create a folder and we're going to open
|
| 151 |
+
that<00:00:46.079><c> folder</c><00:00:46.480><c> in</c><00:00:46.640><c> the</c><00:00:46.800><c> and</c><00:00:47.200><c> gravity.</c><00:00:47.600><c> So</c><00:00:47.840><c> right</c>
|
| 152 |
+
|
| 153 |
+
00:00:47.990 --> 00:00:48.000 align:start position:0%
|
| 154 |
+
that folder in the and gravity. So right
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
00:00:48.000 --> 00:00:49.990 align:start position:0%
|
| 158 |
+
that folder in the and gravity. So right
|
| 159 |
+
now<00:00:48.160><c> I'm</c><00:00:48.480><c> going</c><00:00:48.559><c> to</c><00:00:48.719><c> click</c><00:00:48.879><c> on</c><00:00:49.120><c> file</c><00:00:49.680><c> then</c>
|
| 160 |
+
|
| 161 |
+
00:00:49.990 --> 00:00:50.000 align:start position:0%
|
| 162 |
+
now I'm going to click on file then
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
00:00:50.000 --> 00:00:52.950 align:start position:0%
|
| 166 |
+
now I'm going to click on file then
|
| 167 |
+
click<00:00:50.239><c> here</c><00:00:50.559><c> open</c><00:00:50.879><c> folder</c><00:00:52.160><c> and</c><00:00:52.480><c> let</c><00:00:52.640><c> me</c><00:00:52.800><c> just</c>
|
| 168 |
+
|
| 169 |
+
00:00:52.950 --> 00:00:52.960 align:start position:0%
|
| 170 |
+
click here open folder and let me just
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
00:00:52.960 --> 00:00:56.310 align:start position:0%
|
| 174 |
+
click here open folder and let me just
|
| 175 |
+
create<00:00:53.199><c> a</c><00:00:53.440><c> folder</c><00:00:53.840><c> over</c><00:00:54.160><c> here.</c><00:00:55.600><c> So</c><00:00:56.000><c> let</c><00:00:56.160><c> me</c>
|
| 176 |
+
|
| 177 |
+
00:00:56.310 --> 00:00:56.320 align:start position:0%
|
| 178 |
+
create a folder over here. So let me
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
00:00:56.320 --> 00:00:58.790 align:start position:0%
|
| 182 |
+
create a folder over here. So let me
|
| 183 |
+
just<00:00:56.559><c> right</c><00:00:56.800><c> click</c><00:00:57.280><c> then</c><00:00:57.520><c> click</c><00:00:57.760><c> on</c><00:00:58.000><c> new</c><00:00:58.559><c> and</c>
|
| 184 |
+
|
| 185 |
+
00:00:58.790 --> 00:00:58.800 align:start position:0%
|
| 186 |
+
just right click then click on new and
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
00:00:58.800 --> 00:01:01.029 align:start position:0%
|
| 190 |
+
just right click then click on new and
|
| 191 |
+
the<00:00:59.039><c> folder</c><00:00:59.440><c> name</c><00:00:59.600><c> will</c><00:00:59.840><c> be</c><00:01:00.000><c> let's</c><00:01:00.320><c> say</c>
|
| 192 |
+
|
| 193 |
+
00:01:01.029 --> 00:01:01.039 align:start position:0%
|
| 194 |
+
the folder name will be let's say
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
00:01:01.039 --> 00:01:03.349 align:start position:0%
|
| 198 |
+
the folder name will be let's say
|
| 199 |
+
anti-gravity
|
| 200 |
+
|
| 201 |
+
00:01:03.349 --> 00:01:03.359 align:start position:0%
|
| 202 |
+
anti-gravity
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
00:01:03.359 --> 00:01:06.550 align:start position:0%
|
| 206 |
+
anti-gravity
|
| 207 |
+
dash<00:01:04.000><c> open</c><00:01:04.400><c> code</c><00:01:05.119><c> then</c><00:01:05.360><c> I'm</c><00:01:05.680><c> going</c><00:01:05.840><c> to</c><00:01:06.240><c> select</c>
|
| 208 |
+
|
| 209 |
+
00:01:06.550 --> 00:01:06.560 align:start position:0%
|
| 210 |
+
dash open code then I'm going to select
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
00:01:06.560 --> 00:01:09.270 align:start position:0%
|
| 214 |
+
dash open code then I'm going to select
|
| 215 |
+
this<00:01:06.799><c> one</c><00:01:07.040><c> then</c><00:01:07.280><c> click</c><00:01:07.439><c> here</c><00:01:07.760><c> select</c><00:01:08.080><c> folder</c>
|
| 216 |
+
|
| 217 |
+
00:01:09.270 --> 00:01:09.280 align:start position:0%
|
| 218 |
+
this one then click here select folder
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
00:01:09.280 --> 00:01:10.950 align:start position:0%
|
| 222 |
+
this one then click here select folder
|
| 223 |
+
and<00:01:09.439><c> there</c><00:01:09.680><c> you</c><00:01:09.840><c> can</c><00:01:09.920><c> see</c><00:01:10.080><c> this</c><00:01:10.320><c> folder</c><00:01:10.720><c> is</c>
|
| 224 |
+
|
| 225 |
+
00:01:10.950 --> 00:01:10.960 align:start position:0%
|
| 226 |
+
and there you can see this folder is
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
00:01:10.960 --> 00:01:13.750 align:start position:0%
|
| 230 |
+
and there you can see this folder is
|
| 231 |
+
opened<00:01:11.439><c> over</c><00:01:11.760><c> here.</c><00:01:12.799><c> So</c><00:01:13.040><c> right</c><00:01:13.200><c> now</c><00:01:13.439><c> we're</c>
|
| 232 |
+
|
| 233 |
+
00:01:13.750 --> 00:01:13.760 align:start position:0%
|
| 234 |
+
opened over here. So right now we're
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
00:01:13.760 --> 00:01:18.230 align:start position:0%
|
| 238 |
+
opened over here. So right now we're
|
| 239 |
+
going<00:01:14.000><c> to</c><00:01:14.479><c> install</c><00:01:15.280><c> open</c><00:01:15.680><c> code</c><00:01:16.159><c> in</c><00:01:16.479><c> the</c><00:01:16.960><c> antiD.</c>
|
| 240 |
+
|
| 241 |
+
00:01:18.230 --> 00:01:18.240 align:start position:0%
|
| 242 |
+
going to install open code in the antiD.
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
00:01:18.240 --> 00:01:20.310 align:start position:0%
|
| 246 |
+
going to install open code in the antiD.
|
| 247 |
+
So<00:01:18.479><c> how</c><00:01:18.640><c> can</c><00:01:18.799><c> we</c><00:01:18.960><c> do</c><00:01:19.040><c> it?</c><00:01:19.280><c> So</c><00:01:19.439><c> for</c><00:01:19.600><c> it</c><00:01:19.840><c> here</c><00:01:20.080><c> you</c>
|
| 248 |
+
|
| 249 |
+
00:01:20.310 --> 00:01:20.320 align:start position:0%
|
| 250 |
+
So how can we do it? So for it here you
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
00:01:20.320 --> 00:01:22.870 align:start position:0%
|
| 254 |
+
So how can we do it? So for it here you
|
| 255 |
+
can<00:01:20.479><c> see</c><00:01:20.720><c> extensions</c><00:01:21.600><c> option</c><00:01:22.080><c> here.</c><00:01:22.479><c> So</c><00:01:22.640><c> you</c>
|
| 256 |
+
|
| 257 |
+
00:01:22.870 --> 00:01:22.880 align:start position:0%
|
| 258 |
+
can see extensions option here. So you
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
00:01:22.880 --> 00:01:25.270 align:start position:0%
|
| 262 |
+
can see extensions option here. So you
|
| 263 |
+
need<00:01:22.960><c> to</c><00:01:23.200><c> click</c><00:01:23.520><c> here</c><00:01:24.240><c> and</c><00:01:24.560><c> in</c><00:01:24.799><c> the</c><00:01:24.960><c> search</c>
|
| 264 |
+
|
| 265 |
+
00:01:25.270 --> 00:01:25.280 align:start position:0%
|
| 266 |
+
need to click here and in the search
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
00:01:25.280 --> 00:01:28.390 align:start position:0%
|
| 270 |
+
need to click here and in the search
|
| 271 |
+
option<00:01:25.680><c> you</c><00:01:25.920><c> need</c><00:01:26.080><c> to</c><00:01:26.240><c> search</c><00:01:26.560><c> for</c><00:01:27.280><c> open</c><00:01:27.680><c> code.</c>
|
| 272 |
+
|
| 273 |
+
00:01:28.390 --> 00:01:28.400 align:start position:0%
|
| 274 |
+
option you need to search for open code.
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
00:01:28.400 --> 00:01:30.469 align:start position:0%
|
| 278 |
+
option you need to search for open code.
|
| 279 |
+
So<00:01:28.640><c> once</c><00:01:28.960><c> you</c><00:01:29.119><c> search</c><00:01:29.439><c> for</c><00:01:29.680><c> open</c><00:01:30.000><c> code</c><00:01:30.320><c> you</c>
|
| 280 |
+
|
| 281 |
+
00:01:30.469 --> 00:01:30.479 align:start position:0%
|
| 282 |
+
So once you search for open code you
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
00:01:30.479 --> 00:01:32.630 align:start position:0%
|
| 286 |
+
So once you search for open code you
|
| 287 |
+
will<00:01:30.720><c> see</c><00:01:30.880><c> this</c><00:01:31.280><c> icon</c><00:01:31.600><c> over</c><00:01:31.840><c> here.</c><00:01:32.240><c> Here</c><00:01:32.400><c> you</c>
|
| 288 |
+
|
| 289 |
+
00:01:32.630 --> 00:01:32.640 align:start position:0%
|
| 290 |
+
will see this icon over here. Here you
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
00:01:32.640 --> 00:01:35.190 align:start position:0%
|
| 294 |
+
will see this icon over here. Here you
|
| 295 |
+
can<00:01:32.720><c> see</c><00:01:32.960><c> open</c><00:01:33.280><c> code.</c><00:01:34.079><c> So</c><00:01:34.320><c> we</c><00:01:34.560><c> need</c><00:01:34.640><c> to</c><00:01:34.880><c> click</c>
|
| 296 |
+
|
| 297 |
+
00:01:35.190 --> 00:01:35.200 align:start position:0%
|
| 298 |
+
can see open code. So we need to click
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
00:01:35.200 --> 00:01:37.030 align:start position:0%
|
| 302 |
+
can see open code. So we need to click
|
| 303 |
+
here<00:01:35.680><c> and</c><00:01:35.920><c> there</c><00:01:36.159><c> you</c><00:01:36.320><c> can</c><00:01:36.479><c> see</c><00:01:36.640><c> install</c>
|
| 304 |
+
|
| 305 |
+
00:01:37.030 --> 00:01:37.040 align:start position:0%
|
| 306 |
+
here and there you can see install
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
00:01:37.040 --> 00:01:39.990 align:start position:0%
|
| 310 |
+
here and there you can see install
|
| 311 |
+
option.<00:01:37.520><c> So</c><00:01:37.680><c> you</c><00:01:37.840><c> need</c><00:01:38.000><c> to</c><00:01:38.159><c> click</c><00:01:38.400><c> on</c><00:01:38.640><c> install</c>
|
| 312 |
+
|
| 313 |
+
00:01:39.990 --> 00:01:40.000 align:start position:0%
|
| 314 |
+
option. So you need to click on install
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
00:01:40.000 --> 00:01:42.630 align:start position:0%
|
| 318 |
+
option. So you need to click on install
|
| 319 |
+
and<00:01:40.240><c> there</c><00:01:40.400><c> you</c><00:01:40.640><c> can</c><00:01:40.720><c> see</c><00:01:40.880><c> it</c><00:01:40.960><c> is</c><00:01:41.360><c> installed.</c>
|
| 320 |
+
|
| 321 |
+
00:01:42.630 --> 00:01:42.640 align:start position:0%
|
| 322 |
+
and there you can see it is installed.
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
00:01:42.640 --> 00:01:45.670 align:start position:0%
|
| 326 |
+
and there you can see it is installed.
|
| 327 |
+
So<00:01:42.880><c> after</c><00:01:43.200><c> that</c><00:01:43.600><c> in</c><00:01:43.840><c> order</c><00:01:44.000><c> to</c><00:01:44.240><c> open</c><00:01:44.960><c> the</c><00:01:45.439><c> open</c>
|
| 328 |
+
|
| 329 |
+
00:01:45.670 --> 00:01:45.680 align:start position:0%
|
| 330 |
+
So after that in order to open the open
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
00:01:45.680 --> 00:01:48.310 align:start position:0%
|
| 334 |
+
So after that in order to open the open
|
| 335 |
+
code<00:01:46.000><c> CLI</c><00:01:46.720><c> here</c><00:01:47.119><c> at</c><00:01:47.360><c> the</c><00:01:47.520><c> top</c><00:01:47.759><c> you</c><00:01:48.000><c> will</c><00:01:48.159><c> see</c>
|
| 336 |
+
|
| 337 |
+
00:01:48.310 --> 00:01:48.320 align:start position:0%
|
| 338 |
+
code CLI here at the top you will see
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
00:01:48.320 --> 00:01:50.630 align:start position:0%
|
| 342 |
+
code CLI here at the top you will see
|
| 343 |
+
this<00:01:48.720><c> icon</c><00:01:48.960><c> over</c><00:01:49.280><c> here.</c><00:01:49.600><c> The</c><00:01:49.840><c> thing</c><00:01:50.000><c> is</c><00:01:50.320><c> if</c><00:01:50.479><c> you</c>
|
| 344 |
+
|
| 345 |
+
00:01:50.630 --> 00:01:50.640 align:start position:0%
|
| 346 |
+
this icon over here. The thing is if you
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
00:01:50.640 --> 00:01:52.789 align:start position:0%
|
| 350 |
+
this icon over here. The thing is if you
|
| 351 |
+
now<00:01:50.880><c> just</c><00:01:51.119><c> close</c><00:01:51.439><c> this</c><00:01:51.920><c> you</c><00:01:52.159><c> won't</c><00:01:52.399><c> be</c><00:01:52.479><c> able</c><00:01:52.720><c> to</c>
|
| 352 |
+
|
| 353 |
+
00:01:52.789 --> 00:01:52.799 align:start position:0%
|
| 354 |
+
now just close this you won't be able to
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
00:01:52.799 --> 00:01:55.270 align:start position:0%
|
| 358 |
+
now just close this you won't be able to
|
| 359 |
+
see<00:01:52.960><c> that</c><00:01:53.360><c> icon.</c><00:01:53.680><c> So</c><00:01:53.920><c> you</c><00:01:54.079><c> need</c><00:01:54.320><c> to</c><00:01:54.799><c> go</c><00:01:54.960><c> to</c><00:01:55.119><c> the</c>
|
| 360 |
+
|
| 361 |
+
00:01:55.270 --> 00:01:55.280 align:start position:0%
|
| 362 |
+
see that icon. So you need to go to the
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
00:01:55.280 --> 00:01:57.590 align:start position:0%
|
| 366 |
+
see that icon. So you need to go to the
|
| 367 |
+
extension<00:01:56.159><c> uh</c><00:01:56.320><c> search</c><00:01:56.479><c> for</c><00:01:56.799><c> open</c><00:01:57.119><c> code</c><00:01:57.360><c> then</c>
|
| 368 |
+
|
| 369 |
+
00:01:57.590 --> 00:01:57.600 align:start position:0%
|
| 370 |
+
extension uh search for open code then
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
00:01:57.600 --> 00:01:59.749 align:start position:0%
|
| 374 |
+
extension uh search for open code then
|
| 375 |
+
click<00:01:57.920><c> here</c><00:01:58.479><c> and</c><00:01:58.719><c> then</c><00:01:58.960><c> you'll</c><00:01:59.280><c> be</c><00:01:59.360><c> able</c><00:01:59.520><c> to</c>
|
| 376 |
+
|
| 377 |
+
00:01:59.749 --> 00:01:59.759 align:start position:0%
|
| 378 |
+
click here and then you'll be able to
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
00:01:59.759 --> 00:02:02.230 align:start position:0%
|
| 382 |
+
click here and then you'll be able to
|
| 383 |
+
see<00:01:59.920><c> the</c><00:02:00.719><c> icon</c><00:02:00.960><c> here.</c><00:02:01.360><c> So</c><00:02:01.520><c> you</c><00:02:01.680><c> need</c><00:02:01.840><c> to</c><00:02:02.000><c> click</c>
|
| 384 |
+
|
| 385 |
+
00:02:02.230 --> 00:02:02.240 align:start position:0%
|
| 386 |
+
see the icon here. So you need to click
|
| 387 |
+
|
| 388 |
+
|
| 389 |
+
00:02:02.240 --> 00:02:04.870 align:start position:0%
|
| 390 |
+
see the icon here. So you need to click
|
| 391 |
+
on<00:02:02.399><c> it</c><00:02:02.640><c> and</c><00:02:02.880><c> it</c><00:02:03.119><c> will</c><00:02:03.360><c> open</c><00:02:03.600><c> the</c><00:02:03.840><c> open</c><00:02:04.159><c> code</c><00:02:04.399><c> CLI</c>
|
| 392 |
+
|
| 393 |
+
00:02:04.870 --> 00:02:04.880 align:start position:0%
|
| 394 |
+
on it and it will open the open code CLI
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
00:02:04.880 --> 00:02:07.109 align:start position:0%
|
| 398 |
+
on it and it will open the open code CLI
|
| 399 |
+
over<00:02:05.119><c> here.</c><00:02:05.840><c> So</c><00:02:06.000><c> there</c><00:02:06.159><c> you</c><00:02:06.399><c> can</c><00:02:06.479><c> see.</c><00:02:06.719><c> So</c><00:02:06.960><c> now</c>
|
| 400 |
+
|
| 401 |
+
00:02:07.109 --> 00:02:07.119 align:start position:0%
|
| 402 |
+
over here. So there you can see. So now
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
00:02:07.119 --> 00:02:09.270 align:start position:0%
|
| 406 |
+
over here. So there you can see. So now
|
| 407 |
+
I<00:02:07.360><c> can</c><00:02:07.520><c> just</c><00:02:07.759><c> close</c><00:02:08.000><c> this</c><00:02:08.319><c> and</c><00:02:08.560><c> let</c><00:02:08.800><c> me</c><00:02:08.879><c> just</c><00:02:09.119><c> go</c>
|
| 408 |
+
|
| 409 |
+
00:02:09.270 --> 00:02:09.280 align:start position:0%
|
| 410 |
+
I can just close this and let me just go
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
00:02:09.280 --> 00:02:11.830 align:start position:0%
|
| 414 |
+
I can just close this and let me just go
|
| 415 |
+
to<00:02:09.440><c> my</c><00:02:09.599><c> folder</c><00:02:10.000><c> location.</c>
|
| 416 |
+
|
| 417 |
+
00:02:11.830 --> 00:02:11.840 align:start position:0%
|
| 418 |
+
to my folder location.
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
00:02:11.840 --> 00:02:13.510 align:start position:0%
|
| 422 |
+
to my folder location.
|
| 423 |
+
So<00:02:12.160><c> first</c><00:02:12.400><c> of</c><00:02:12.480><c> all</c><00:02:12.640><c> what</c><00:02:12.879><c> we</c><00:02:13.040><c> are</c><00:02:13.120><c> going</c><00:02:13.280><c> to</c><00:02:13.360><c> do</c>
|
| 424 |
+
|
| 425 |
+
00:02:13.510 --> 00:02:13.520 align:start position:0%
|
| 426 |
+
So first of all what we are going to do
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
00:02:13.520 --> 00:02:17.270 align:start position:0%
|
| 430 |
+
So first of all what we are going to do
|
| 431 |
+
is<00:02:13.680><c> that</c><00:02:14.000><c> we're</c><00:02:14.319><c> going</c><00:02:14.640><c> to</c><00:02:15.520><c> generate</c><00:02:16.239><c> a</c><00:02:16.720><c> plan</c>
|
| 432 |
+
|
| 433 |
+
00:02:17.270 --> 00:02:17.280 align:start position:0%
|
| 434 |
+
is that we're going to generate a plan
|
| 435 |
+
|
| 436 |
+
|
| 437 |
+
00:02:17.280 --> 00:02:19.670 align:start position:0%
|
| 438 |
+
is that we're going to generate a plan
|
| 439 |
+
using<00:02:17.760><c> anti-gravity</c><00:02:18.720><c> and</c><00:02:18.959><c> we</c><00:02:19.120><c> are</c><00:02:19.280><c> going</c><00:02:19.440><c> to</c>
|
| 440 |
+
|
| 441 |
+
00:02:19.670 --> 00:02:19.680 align:start position:0%
|
| 442 |
+
using anti-gravity and we are going to
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
00:02:19.680 --> 00:02:22.790 align:start position:0%
|
| 446 |
+
using anti-gravity and we are going to
|
| 447 |
+
execute<00:02:20.319><c> that</c><00:02:20.640><c> plan</c><00:02:21.040><c> using</c><00:02:21.599><c> open</c><00:02:21.920><c> code.</c><00:02:22.560><c> But</c>
|
| 448 |
+
|
| 449 |
+
00:02:22.790 --> 00:02:22.800 align:start position:0%
|
| 450 |
+
execute that plan using open code. But
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
00:02:22.800 --> 00:02:23.990 align:start position:0%
|
| 454 |
+
execute that plan using open code. But
|
| 455 |
+
before<00:02:23.040><c> that</c><00:02:23.360><c> here</c><00:02:23.520><c> you</c><00:02:23.760><c> can</c><00:02:23.840><c> see</c>
|
| 456 |
+
|
| 457 |
+
00:02:23.990 --> 00:02:24.000 align:start position:0%
|
| 458 |
+
before that here you can see
|
| 459 |
+
|
| 460 |
+
|
| 461 |
+
00:02:24.000 --> 00:02:26.309 align:start position:0%
|
| 462 |
+
before that here you can see
|
| 463 |
+
anti-gravity<00:02:24.800><c> settings.</c><00:02:25.280><c> So</c><00:02:25.440><c> here</c><00:02:25.920><c> you</c><00:02:26.160><c> need</c>
|
| 464 |
+
|
| 465 |
+
00:02:26.309 --> 00:02:26.319 align:start position:0%
|
| 466 |
+
anti-gravity settings. So here you need
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
00:02:26.319 --> 00:02:28.550 align:start position:0%
|
| 470 |
+
anti-gravity settings. So here you need
|
| 471 |
+
to<00:02:26.480><c> click</c><00:02:26.720><c> on</c><00:02:26.959><c> anti-gravity</c><00:02:27.840><c> setting</c><00:02:28.239><c> and</c>
|
| 472 |
+
|
| 473 |
+
00:02:28.550 --> 00:02:28.560 align:start position:0%
|
| 474 |
+
to click on anti-gravity setting and
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
00:02:28.560 --> 00:02:31.270 align:start position:0%
|
| 478 |
+
to click on anti-gravity setting and
|
| 479 |
+
here<00:02:29.520><c> uh</c><00:02:29.680><c> since</c><00:02:29.920><c> we</c><00:02:30.080><c> are</c><00:02:30.239><c> using</c><00:02:30.560><c> anti-gravity</c>
|
| 480 |
+
|
| 481 |
+
00:02:31.270 --> 00:02:31.280 align:start position:0%
|
| 482 |
+
here uh since we are using anti-gravity
|
| 483 |
+
|
| 484 |
+
|
| 485 |
+
00:02:31.280 --> 00:02:34.229 align:start position:0%
|
| 486 |
+
here uh since we are using anti-gravity
|
| 487 |
+
with<00:02:31.599><c> open</c><00:02:31.920><c> code.</c><00:02:32.319><c> So</c><00:02:32.560><c> for</c><00:02:32.879><c> this</c><00:02:33.599><c> just</c><00:02:33.840><c> make</c><00:02:34.000><c> a</c>
|
| 488 |
+
|
| 489 |
+
00:02:34.229 --> 00:02:34.239 align:start position:0%
|
| 490 |
+
with open code. So for this just make a
|
| 491 |
+
|
| 492 |
+
|
| 493 |
+
00:02:34.239 --> 00:02:36.390 align:start position:0%
|
| 494 |
+
with open code. So for this just make a
|
| 495 |
+
little<00:02:34.400><c> bit</c><00:02:34.560><c> of</c><00:02:34.800><c> changes</c><00:02:35.200><c> and</c><00:02:35.440><c> that</c><00:02:35.680><c> is</c><00:02:36.239><c> here</c>
|
| 496 |
+
|
| 497 |
+
00:02:36.390 --> 00:02:36.400 align:start position:0%
|
| 498 |
+
little bit of changes and that is here
|
| 499 |
+
|
| 500 |
+
|
| 501 |
+
00:02:36.400 --> 00:02:38.710 align:start position:0%
|
| 502 |
+
little bit of changes and that is here
|
| 503 |
+
you<00:02:36.640><c> can</c><00:02:36.720><c> see</c><00:02:37.040><c> auto</c><00:02:37.519><c> execution.</c><00:02:38.160><c> Here</c><00:02:38.400><c> you</c><00:02:38.560><c> can</c>
|
| 504 |
+
|
| 505 |
+
00:02:38.710 --> 00:02:38.720 align:start position:0%
|
| 506 |
+
you can see auto execution. Here you can
|
| 507 |
+
|
| 508 |
+
|
| 509 |
+
00:02:38.720 --> 00:02:42.150 align:start position:0%
|
| 510 |
+
you can see auto execution. Here you can
|
| 511 |
+
see<00:02:38.959><c> you</c><00:02:39.200><c> need</c><00:02:39.440><c> to</c><00:02:40.080><c> make</c><00:02:40.319><c> it</c><00:02:40.879><c> uh</c><00:02:41.200><c> request</c>
|
| 512 |
+
|
| 513 |
+
00:02:42.150 --> 00:02:42.160 align:start position:0%
|
| 514 |
+
see you need to make it uh request
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
00:02:42.160 --> 00:02:45.270 align:start position:0%
|
| 518 |
+
see you need to make it uh request
|
| 519 |
+
review<00:02:43.120><c> and</c><00:02:43.360><c> then</c><00:02:43.599><c> choose</c><00:02:44.000><c> this</c><00:02:44.720><c> request</c>
|
| 520 |
+
|
| 521 |
+
00:02:45.270 --> 00:02:45.280 align:start position:0%
|
| 522 |
+
review and then choose this request
|
| 523 |
+
|
| 524 |
+
|
| 525 |
+
00:02:45.280 --> 00:02:48.470 align:start position:0%
|
| 526 |
+
review and then choose this request
|
| 527 |
+
review<00:02:45.680><c> as</c><00:02:45.920><c> well</c><00:02:46.720><c> and</c><00:02:47.040><c> just</c><00:02:47.519><c> uh</c><00:02:47.680><c> do</c><00:02:47.920><c> this</c><00:02:48.160><c> when</c>
|
| 528 |
+
|
| 529 |
+
00:02:48.470 --> 00:02:48.480 align:start position:0%
|
| 530 |
+
review as well and just uh do this when
|
| 531 |
+
|
| 532 |
+
|
| 533 |
+
00:02:48.480 --> 00:02:50.710 align:start position:0%
|
| 534 |
+
review as well and just uh do this when
|
| 535 |
+
you<00:02:48.720><c> are</c><00:02:48.879><c> using</c><00:02:49.360><c> anti-gravity</c><00:02:50.080><c> with</c><00:02:50.400><c> open</c>
|
| 536 |
+
|
| 537 |
+
00:02:50.710 --> 00:02:50.720 align:start position:0%
|
| 538 |
+
you are using anti-gravity with open
|
| 539 |
+
|
| 540 |
+
|
| 541 |
+
00:02:50.720 --> 00:02:53.270 align:start position:0%
|
| 542 |
+
you are using anti-gravity with open
|
| 543 |
+
code.<00:02:51.120><c> Okay.</c><00:02:51.680><c> So</c><00:02:51.920><c> after</c><00:02:52.160><c> that</c><00:02:52.480><c> just</c><00:02:52.800><c> minimize</c>
|
| 544 |
+
|
| 545 |
+
00:02:53.270 --> 00:02:53.280 align:start position:0%
|
| 546 |
+
code. Okay. So after that just minimize
|
| 547 |
+
|
| 548 |
+
|
| 549 |
+
00:02:53.280 --> 00:02:56.229 align:start position:0%
|
| 550 |
+
code. Okay. So after that just minimize
|
| 551 |
+
that<00:02:53.680><c> and</c><00:02:53.920><c> then</c><00:02:54.560><c> I'm</c><00:02:54.800><c> just</c><00:02:55.040><c> going</c><00:02:55.200><c> to</c><00:02:55.360><c> write</c><00:02:55.680><c> a</c>
|
| 552 |
+
|
| 553 |
+
00:02:56.229 --> 00:02:56.239 align:start position:0%
|
| 554 |
+
that and then I'm just going to write a
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
00:02:56.239 --> 00:02:58.710 align:start position:0%
|
| 558 |
+
that and then I'm just going to write a
|
| 559 |
+
prompt<00:02:56.640><c> over</c><00:02:56.879><c> here</c><00:02:57.200><c> and</c><00:02:57.440><c> that</c><00:02:57.760><c> prompt</c><00:02:58.080><c> is</c><00:02:58.480><c> I</c>
|
| 560 |
+
|
| 561 |
+
00:02:58.710 --> 00:02:58.720 align:start position:0%
|
| 562 |
+
prompt over here and that prompt is I
|
| 563 |
+
|
| 564 |
+
|
| 565 |
+
00:02:58.720 --> 00:03:00.470 align:start position:0%
|
| 566 |
+
prompt over here and that prompt is I
|
| 567 |
+
want<00:02:58.879><c> to</c><00:02:59.040><c> build</c><00:02:59.200><c> a</c><00:02:59.440><c> inventory</c><00:03:00.000><c> management</c>
|
| 568 |
+
|
| 569 |
+
00:03:00.470 --> 00:03:00.480 align:start position:0%
|
| 570 |
+
want to build a inventory management
|
| 571 |
+
|
| 572 |
+
|
| 573 |
+
00:03:00.480 --> 00:03:03.830 align:start position:0%
|
| 574 |
+
want to build a inventory management
|
| 575 |
+
system<00:03:00.959><c> using</c><00:03:01.680><c> Django</c><00:03:02.239><c> and</c><00:03:02.720><c> SQLite.</c><00:03:03.440><c> I</c><00:03:03.760><c> want</c>
|
| 576 |
+
|
| 577 |
+
00:03:03.830 --> 00:03:03.840 align:start position:0%
|
| 578 |
+
system using Django and SQLite. I want
|
| 579 |
+
|
| 580 |
+
|
| 581 |
+
00:03:03.840 --> 00:03:06.070 align:start position:0%
|
| 582 |
+
system using Django and SQLite. I want
|
| 583 |
+
the<00:03:04.000><c> admin</c><00:03:04.319><c> panel</c><00:03:04.640><c> design</c><00:03:04.959><c> to</c><00:03:05.200><c> be</c><00:03:05.280><c> modern</c><00:03:05.920><c> and</c>
|
| 584 |
+
|
| 585 |
+
00:03:06.070 --> 00:03:06.080 align:start position:0%
|
| 586 |
+
the admin panel design to be modern and
|
| 587 |
+
|
| 588 |
+
|
| 589 |
+
00:03:06.080 --> 00:03:10.309 align:start position:0%
|
| 590 |
+
the admin panel design to be modern and
|
| 591 |
+
with<00:03:07.040><c> fully</c><00:03:07.440><c> functional.</c><00:03:08.640><c> Do</c><00:03:08.879><c> not</c><00:03:09.040><c> use</c><00:03:09.440><c> Django</c>
|
| 592 |
+
|
| 593 |
+
00:03:10.309 --> 00:03:10.319 align:start position:0%
|
| 594 |
+
with fully functional. Do not use Django
|
| 595 |
+
|
| 596 |
+
|
| 597 |
+
00:03:10.319 --> 00:03:13.110 align:start position:0%
|
| 598 |
+
with fully functional. Do not use Django
|
| 599 |
+
uh<00:03:10.480><c> default</c><00:03:10.879><c> admin</c><00:03:11.280><c> panel.</c><00:03:12.000><c> So</c><00:03:12.159><c> I</c><00:03:12.480><c> want</c><00:03:12.720><c> to</c>
|
| 600 |
+
|
| 601 |
+
00:03:13.110 --> 00:03:13.120 align:start position:0%
|
| 602 |
+
uh default admin panel. So I want to
|
| 603 |
+
|
| 604 |
+
|
| 605 |
+
00:03:13.120 --> 00:03:14.790 align:start position:0%
|
| 606 |
+
uh default admin panel. So I want to
|
| 607 |
+
make<00:03:13.360><c> a</c><00:03:13.519><c> plan</c><00:03:13.840><c> for</c><00:03:14.000><c> this.</c><00:03:14.239><c> So</c><00:03:14.319><c> I'm</c><00:03:14.560><c> going</c><00:03:14.640><c> to</c>
|
| 608 |
+
|
| 609 |
+
00:03:14.790 --> 00:03:14.800 align:start position:0%
|
| 610 |
+
make a plan for this. So I'm going to
|
| 611 |
+
|
| 612 |
+
|
| 613 |
+
00:03:14.800 --> 00:03:17.190 align:start position:0%
|
| 614 |
+
make a plan for this. So I'm going to
|
| 615 |
+
select<00:03:15.040><c> the</c><00:03:15.280><c> planning</c><00:03:15.680><c> mode.</c><00:03:16.400><c> Then</c><00:03:16.720><c> for</c><00:03:16.959><c> the</c>
|
| 616 |
+
|
| 617 |
+
00:03:17.190 --> 00:03:17.200 align:start position:0%
|
| 618 |
+
select the planning mode. Then for the
|
| 619 |
+
|
| 620 |
+
|
| 621 |
+
00:03:17.200 --> 00:03:19.270 align:start position:0%
|
| 622 |
+
select the planning mode. Then for the
|
| 623 |
+
thinking<00:03:18.000><c> uh</c><00:03:18.159><c> we</c><00:03:18.319><c> are</c><00:03:18.480><c> going</c><00:03:18.560><c> to</c><00:03:18.640><c> choose</c><00:03:18.959><c> this</c>
|
| 624 |
+
|
| 625 |
+
00:03:19.270 --> 00:03:19.280 align:start position:0%
|
| 626 |
+
thinking uh we are going to choose this
|
| 627 |
+
|
| 628 |
+
|
| 629 |
+
00:03:19.280 --> 00:03:22.630 align:start position:0%
|
| 630 |
+
thinking uh we are going to choose this
|
| 631 |
+
model<00:03:20.000><c> cloud</c><00:03:20.400><c> opas</c><00:03:20.959><c> 4.5</c><00:03:21.760><c> thinking.</c><00:03:22.239><c> So</c><00:03:22.400><c> this</c>
|
| 632 |
+
|
| 633 |
+
00:03:22.630 --> 00:03:22.640 align:start position:0%
|
| 634 |
+
model cloud opas 4.5 thinking. So this
|
| 635 |
+
|
| 636 |
+
|
| 637 |
+
00:03:22.640 --> 00:03:25.030 align:start position:0%
|
| 638 |
+
model cloud opas 4.5 thinking. So this
|
| 639 |
+
this<00:03:22.879><c> is</c><00:03:23.120><c> really</c><00:03:23.440><c> good</c><00:03:23.680><c> at</c><00:03:23.920><c> generating</c><00:03:24.480><c> plan.</c>
|
| 640 |
+
|
| 641 |
+
00:03:25.030 --> 00:03:25.040 align:start position:0%
|
| 642 |
+
this is really good at generating plan.
|
| 643 |
+
|
| 644 |
+
|
| 645 |
+
00:03:25.040 --> 00:03:27.990 align:start position:0%
|
| 646 |
+
this is really good at generating plan.
|
| 647 |
+
So<00:03:25.200><c> that's</c><00:03:25.440><c> why</c><00:03:25.599><c> I</c><00:03:25.920><c> choose</c><00:03:26.159><c> this</c><00:03:26.400><c> model.</c><00:03:27.760><c> Then</c>
|
| 648 |
+
|
| 649 |
+
00:03:27.990 --> 00:03:28.000 align:start position:0%
|
| 650 |
+
So that's why I choose this model. Then
|
| 651 |
+
|
| 652 |
+
|
| 653 |
+
00:03:28.000 --> 00:03:30.869 align:start position:0%
|
| 654 |
+
So that's why I choose this model. Then
|
| 655 |
+
I'm<00:03:28.239><c> going</c><00:03:28.400><c> to</c><00:03:28.560><c> click</c><00:03:28.720><c> here</c><00:03:29.040><c> send</c><00:03:30.159><c> and</c><00:03:30.400><c> it</c><00:03:30.640><c> will</c>
|
| 656 |
+
|
| 657 |
+
00:03:30.869 --> 00:03:30.879 align:start position:0%
|
| 658 |
+
I'm going to click here send and it will
|
| 659 |
+
|
| 660 |
+
|
| 661 |
+
00:03:30.879 --> 00:03:33.910 align:start position:0%
|
| 662 |
+
I'm going to click here send and it will
|
| 663 |
+
start<00:03:31.599><c> creating</c><00:03:31.920><c> a</c><00:03:32.159><c> plan</c><00:03:32.480><c> for</c><00:03:32.640><c> us.</c><00:03:33.599><c> And</c><00:03:33.760><c> there</c>
|
| 664 |
+
|
| 665 |
+
00:03:33.910 --> 00:03:33.920 align:start position:0%
|
| 666 |
+
start creating a plan for us. And there
|
| 667 |
+
|
| 668 |
+
|
| 669 |
+
00:03:33.920 --> 00:03:37.110 align:start position:0%
|
| 670 |
+
start creating a plan for us. And there
|
| 671 |
+
you<00:03:34.080><c> can</c><00:03:34.159><c> see</c><00:03:34.400><c> it</c><00:03:35.200><c> created</c><00:03:35.599><c> a</c><00:03:35.840><c> tax</c><00:03:36.239><c> file</c><00:03:36.560><c> for</c><00:03:36.799><c> us</c>
|
| 672 |
+
|
| 673 |
+
00:03:37.110 --> 00:03:37.120 align:start position:0%
|
| 674 |
+
you can see it created a tax file for us
|
| 675 |
+
|
| 676 |
+
|
| 677 |
+
00:03:37.120 --> 00:03:39.110 align:start position:0%
|
| 678 |
+
you can see it created a tax file for us
|
| 679 |
+
and<00:03:37.360><c> it</c><00:03:37.599><c> also</c><00:03:37.920><c> created</c><00:03:38.239><c> the</c><00:03:38.480><c> implementation</c>
|
| 680 |
+
|
| 681 |
+
00:03:39.110 --> 00:03:39.120 align:start position:0%
|
| 682 |
+
and it also created the implementation
|
| 683 |
+
|
| 684 |
+
|
| 685 |
+
00:03:39.120 --> 00:03:43.830 align:start position:0%
|
| 686 |
+
and it also created the implementation
|
| 687 |
+
plan<00:03:39.440><c> for</c><00:03:39.680><c> us.</c><00:03:41.120><c> And</c><00:03:41.680><c> uh</c><00:03:41.920><c> since</c><00:03:42.480><c> we</c><00:03:42.879><c> made</c><00:03:43.440><c> those</c>
|
| 688 |
+
|
| 689 |
+
00:03:43.830 --> 00:03:43.840 align:start position:0%
|
| 690 |
+
plan for us. And uh since we made those
|
| 691 |
+
|
| 692 |
+
|
| 693 |
+
00:03:43.840 --> 00:03:47.430 align:start position:0%
|
| 694 |
+
plan for us. And uh since we made those
|
| 695 |
+
two<00:03:44.159><c> autogation</c><00:03:45.280><c> off</c><00:03:45.920><c> that's</c><00:03:46.239><c> why</c><00:03:46.560><c> it</c><00:03:46.879><c> just</c>
|
| 696 |
+
|
| 697 |
+
00:03:47.430 --> 00:03:47.440 align:start position:0%
|
| 698 |
+
two autogation off that's why it just
|
| 699 |
+
|
| 700 |
+
|
| 701 |
+
00:03:47.440 --> 00:03:50.070 align:start position:0%
|
| 702 |
+
two autogation off that's why it just
|
| 703 |
+
stop<00:03:47.920><c> after</c><00:03:48.400><c> making</c><00:03:48.720><c> the</c><00:03:49.360><c> implementation</c>
|
| 704 |
+
|
| 705 |
+
00:03:50.070 --> 00:03:50.080 align:start position:0%
|
| 706 |
+
stop after making the implementation
|
| 707 |
+
|
| 708 |
+
|
| 709 |
+
00:03:50.080 --> 00:03:52.869 align:start position:0%
|
| 710 |
+
stop after making the implementation
|
| 711 |
+
plan.<00:03:50.879><c> So</c><00:03:51.040><c> right</c><00:03:51.280><c> now</c><00:03:51.519><c> here</c><00:03:52.159><c> uh</c><00:03:52.319><c> you</c><00:03:52.560><c> can</c><00:03:52.640><c> just</c>
|
| 712 |
+
|
| 713 |
+
00:03:52.869 --> 00:03:52.879 align:start position:0%
|
| 714 |
+
plan. So right now here uh you can just
|
| 715 |
+
|
| 716 |
+
|
| 717 |
+
00:03:52.879 --> 00:03:54.550 align:start position:0%
|
| 718 |
+
plan. So right now here uh you can just
|
| 719 |
+
give<00:03:53.120><c> the</c><00:03:53.360><c> implementation</c><00:03:53.920><c> plan.</c><00:03:54.159><c> So</c><00:03:54.400><c> right</c>
|
| 720 |
+
|
| 721 |
+
00:03:54.550 --> 00:03:54.560 align:start position:0%
|
| 722 |
+
give the implementation plan. So right
|
| 723 |
+
|
| 724 |
+
|
| 725 |
+
00:03:54.560 --> 00:03:56.470 align:start position:0%
|
| 726 |
+
give the implementation plan. So right
|
| 727 |
+
now<00:03:54.720><c> I'll</c><00:03:55.040><c> just</c><00:03:55.280><c> directly</c><00:03:55.680><c> copy</c><00:03:56.000><c> the</c><00:03:56.159><c> plan</c>
|
| 728 |
+
|
| 729 |
+
00:03:56.470 --> 00:03:56.480 align:start position:0%
|
| 730 |
+
now I'll just directly copy the plan
|
| 731 |
+
|
| 732 |
+
|
| 733 |
+
00:03:56.480 --> 00:03:58.309 align:start position:0%
|
| 734 |
+
now I'll just directly copy the plan
|
| 735 |
+
from<00:03:56.720><c> here</c>
|
| 736 |
+
|
| 737 |
+
00:03:58.309 --> 00:03:58.319 align:start position:0%
|
| 738 |
+
from here
|
| 739 |
+
|
| 740 |
+
|
| 741 |
+
00:03:58.319 --> 00:04:01.030 align:start position:0%
|
| 742 |
+
from here
|
| 743 |
+
and<00:03:58.720><c> I'll</c><00:03:59.120><c> just</c><00:03:59.599><c> copy</c><00:03:59.920><c> it</c><00:04:00.159><c> up</c><00:04:00.319><c> until</c><00:04:00.799><c> this</c>
|
| 744 |
+
|
| 745 |
+
00:04:01.030 --> 00:04:01.040 align:start position:0%
|
| 746 |
+
and I'll just copy it up until this
|
| 747 |
+
|
| 748 |
+
|
| 749 |
+
00:04:01.040 --> 00:04:03.750 align:start position:0%
|
| 750 |
+
and I'll just copy it up until this
|
| 751 |
+
part.<00:04:01.439><c> So</c><00:04:01.519><c> I'll</c><00:04:01.760><c> just</c><00:04:01.920><c> copy</c><00:04:02.239><c> it.</c><00:04:02.480><c> Ctrl</c><00:04:03.040><c> C.</c><00:04:03.599><c> So</c>
|
| 752 |
+
|
| 753 |
+
00:04:03.750 --> 00:04:03.760 align:start position:0%
|
| 754 |
+
part. So I'll just copy it. Ctrl C. So
|
| 755 |
+
|
| 756 |
+
|
| 757 |
+
00:04:03.760 --> 00:04:06.309 align:start position:0%
|
| 758 |
+
part. So I'll just copy it. Ctrl C. So
|
| 759 |
+
after<00:04:04.159><c> copying</c><00:04:04.480><c> the</c><00:04:04.640><c> plan</c><00:04:05.040><c> I'm</c><00:04:05.280><c> just</c><00:04:05.519><c> going</c><00:04:05.760><c> to</c>
|
| 760 |
+
|
| 761 |
+
00:04:06.309 --> 00:04:06.319 align:start position:0%
|
| 762 |
+
after copying the plan I'm just going to
|
| 763 |
+
|
| 764 |
+
|
| 765 |
+
00:04:06.319 --> 00:04:09.030 align:start position:0%
|
| 766 |
+
after copying the plan I'm just going to
|
| 767 |
+
close<00:04:06.720><c> those</c><00:04:07.200><c> and</c><00:04:07.439><c> I'm</c><00:04:07.760><c> going</c><00:04:07.840><c> to</c><00:04:08.000><c> paste</c><00:04:08.400><c> the</c>
|
| 768 |
+
|
| 769 |
+
00:04:09.030 --> 00:04:09.040 align:start position:0%
|
| 770 |
+
close those and I'm going to paste the
|
| 771 |
+
|
| 772 |
+
|
| 773 |
+
00:04:09.040 --> 00:04:11.190 align:start position:0%
|
| 774 |
+
close those and I'm going to paste the
|
| 775 |
+
prompt<00:04:09.519><c> in</c><00:04:09.760><c> the</c><00:04:10.000><c> open</c><00:04:10.319><c> code.</c><00:04:10.640><c> And</c><00:04:10.879><c> one</c><00:04:11.040><c> more</c>
|
| 776 |
+
|
| 777 |
+
00:04:11.190 --> 00:04:11.200 align:start position:0%
|
| 778 |
+
prompt in the open code. And one more
|
| 779 |
+
|
| 780 |
+
|
| 781 |
+
00:04:11.200 --> 00:04:12.949 align:start position:0%
|
| 782 |
+
prompt in the open code. And one more
|
| 783 |
+
thing<00:04:11.360><c> that</c><00:04:11.519><c> is</c><00:04:11.760><c> if</c><00:04:11.920><c> I</c><00:04:12.000><c> just</c><00:04:12.319><c> over</c><00:04:12.560><c> here</c>
|
| 784 |
+
|
| 785 |
+
00:04:12.949 --> 00:04:12.959 align:start position:0%
|
| 786 |
+
thing that is if I just over here
|
| 787 |
+
|
| 788 |
+
|
| 789 |
+
00:04:12.959 --> 00:04:16.789 align:start position:0%
|
| 790 |
+
thing that is if I just over here
|
| 791 |
+
/models<00:04:14.000><c> then</c><00:04:14.319><c> press</c><00:04:14.879><c> enter</c><00:04:16.079><c> it</c><00:04:16.320><c> will</c><00:04:16.479><c> show</c><00:04:16.639><c> us</c>
|
| 792 |
+
|
| 793 |
+
00:04:16.789 --> 00:04:16.799 align:start position:0%
|
| 794 |
+
/models then press enter it will show us
|
| 795 |
+
|
| 796 |
+
|
| 797 |
+
00:04:16.799 --> 00:04:18.710 align:start position:0%
|
| 798 |
+
/models then press enter it will show us
|
| 799 |
+
all<00:04:17.040><c> the</c><00:04:17.199><c> available</c><00:04:17.680><c> model</c><00:04:18.000><c> over</c><00:04:18.239><c> here.</c><00:04:18.560><c> You</c>
|
| 800 |
+
|
| 801 |
+
00:04:18.710 --> 00:04:18.720 align:start position:0%
|
| 802 |
+
all the available model over here. You
|
| 803 |
+
|
| 804 |
+
|
| 805 |
+
00:04:18.720 --> 00:04:20.789 align:start position:0%
|
| 806 |
+
all the available model over here. You
|
| 807 |
+
can<00:04:18.880><c> see</c><00:04:19.280><c> and</c><00:04:19.519><c> if</c><00:04:19.680><c> you</c><00:04:19.840><c> want</c><00:04:19.919><c> to</c><00:04:20.079><c> see</c><00:04:20.320><c> all</c><00:04:20.560><c> the</c>
|
| 808 |
+
|
| 809 |
+
00:04:20.789 --> 00:04:20.799 align:start position:0%
|
| 810 |
+
can see and if you want to see all the
|
| 811 |
+
|
| 812 |
+
|
| 813 |
+
00:04:20.799 --> 00:04:24.950 align:start position:0%
|
| 814 |
+
can see and if you want to see all the
|
| 815 |
+
providers<00:04:22.000><c> you</c><00:04:22.240><c> need</c><00:04:22.400><c> to</c><00:04:22.560><c> press</c><00:04:22.960><c> Ctrl</c><00:04:23.280><c> +</c><00:04:23.680><c> A</c><00:04:24.639><c> but</c>
|
| 816 |
+
|
| 817 |
+
00:04:24.950 --> 00:04:24.960 align:start position:0%
|
| 818 |
+
providers you need to press Ctrl + A but
|
| 819 |
+
|
| 820 |
+
|
| 821 |
+
00:04:24.960 --> 00:04:27.270 align:start position:0%
|
| 822 |
+
providers you need to press Ctrl + A but
|
| 823 |
+
we<00:04:25.120><c> are</c><00:04:25.280><c> going</c><00:04:25.440><c> to</c><00:04:25.680><c> choose</c><00:04:25.919><c> this</c><00:04:26.240><c> model</c><00:04:26.639><c> Bigple</c>
|
| 824 |
+
|
| 825 |
+
00:04:27.270 --> 00:04:27.280 align:start position:0%
|
| 826 |
+
we are going to choose this model Bigple
|
| 827 |
+
|
| 828 |
+
|
| 829 |
+
00:04:27.280 --> 00:04:29.430 align:start position:0%
|
| 830 |
+
we are going to choose this model Bigple
|
| 831 |
+
which<00:04:27.520><c> is</c><00:04:27.759><c> completely</c><00:04:28.320><c> free</c><00:04:28.639><c> and</c><00:04:28.880><c> there</c><00:04:29.040><c> is</c><00:04:29.199><c> no</c>
|
| 832 |
+
|
| 833 |
+
00:04:29.430 --> 00:04:29.440 align:start position:0%
|
| 834 |
+
which is completely free and there is no
|
| 835 |
+
|
| 836 |
+
|
| 837 |
+
00:04:29.440 --> 00:04:32.150 align:start position:0%
|
| 838 |
+
which is completely free and there is no
|
| 839 |
+
limit<00:04:29.759><c> of</c><00:04:30.000><c> how</c><00:04:30.240><c> much</c><00:04:30.479><c> you</c><00:04:30.720><c> can</c><00:04:30.960><c> use.</c><00:04:31.759><c> So</c><00:04:31.919><c> I'm</c>
|
| 840 |
+
|
| 841 |
+
00:04:32.150 --> 00:04:32.160 align:start position:0%
|
| 842 |
+
limit of how much you can use. So I'm
|
| 843 |
+
|
| 844 |
+
|
| 845 |
+
00:04:32.160 --> 00:04:34.550 align:start position:0%
|
| 846 |
+
limit of how much you can use. So I'm
|
| 847 |
+
just<00:04:32.320><c> going</c><00:04:32.479><c> to</c><00:04:32.960><c> uh</c><00:04:33.120><c> choose</c><00:04:33.520><c> big</c><00:04:33.759><c> pickle</c><00:04:34.320><c> and</c>
|
| 848 |
+
|
| 849 |
+
00:04:34.550 --> 00:04:34.560 align:start position:0%
|
| 850 |
+
just going to uh choose big pickle and
|
| 851 |
+
|
| 852 |
+
|
| 853 |
+
00:04:34.560 --> 00:04:37.350 align:start position:0%
|
| 854 |
+
just going to uh choose big pickle and
|
| 855 |
+
here<00:04:34.880><c> I'm</c><00:04:35.120><c> going</c><00:04:35.280><c> to</c><00:04:35.440><c> paste</c><00:04:35.840><c> the</c><00:04:36.720><c> prompt.</c><00:04:37.199><c> So</c>
|
| 856 |
+
|
| 857 |
+
00:04:37.350 --> 00:04:37.360 align:start position:0%
|
| 858 |
+
here I'm going to paste the prompt. So
|
| 859 |
+
|
| 860 |
+
|
| 861 |
+
00:04:37.360 --> 00:04:40.629 align:start position:0%
|
| 862 |
+
here I'm going to paste the prompt. So
|
| 863 |
+
I'll<00:04:37.600><c> just</c><00:04:37.759><c> press</c><00:04:38.160><c> Ctrl</c><00:04:38.960><c> E</c><00:04:39.919><c> and</c><00:04:40.160><c> there</c><00:04:40.400><c> you</c><00:04:40.560><c> can</c>
|
| 864 |
+
|
| 865 |
+
00:04:40.629 --> 00:04:40.639 align:start position:0%
|
| 866 |
+
I'll just press Ctrl E and there you can
|
| 867 |
+
|
| 868 |
+
|
| 869 |
+
00:04:40.639 --> 00:04:44.550 align:start position:0%
|
| 870 |
+
I'll just press Ctrl E and there you can
|
| 871 |
+
see<00:04:40.880><c> pasted</c><00:04:41.919><c> 120</c><00:04:42.560><c> lines.</c>
|
| 872 |
+
|
| 873 |
+
00:04:44.550 --> 00:04:44.560 align:start position:0%
|
| 874 |
+
see pasted 120 lines.
|
| 875 |
+
|
| 876 |
+
|
| 877 |
+
00:04:44.560 --> 00:04:48.550 align:start position:0%
|
| 878 |
+
see pasted 120 lines.
|
| 879 |
+
So<00:04:45.440><c> we</c><00:04:45.680><c> are</c><00:04:45.840><c> going</c><00:04:46.000><c> to</c><00:04:46.160><c> press</c><00:04:46.560><c> enter</c><00:04:47.759><c> and</c><00:04:48.240><c> open</c>
|
| 880 |
+
|
| 881 |
+
00:04:48.550 --> 00:04:48.560 align:start position:0%
|
| 882 |
+
So we are going to press enter and open
|
| 883 |
+
|
| 884 |
+
|
| 885 |
+
00:04:48.560 --> 00:04:51.510 align:start position:0%
|
| 886 |
+
So we are going to press enter and open
|
| 887 |
+
code<00:04:48.880><c> will</c><00:04:49.360><c> start</c><00:04:49.840><c> executing</c><00:04:50.479><c> the</c><00:04:50.960><c> plan</c><00:04:51.280><c> for</c>
|
| 888 |
+
|
| 889 |
+
00:04:51.510 --> 00:04:51.520 align:start position:0%
|
| 890 |
+
code will start executing the plan for
|
| 891 |
+
|
| 892 |
+
|
| 893 |
+
00:04:51.520 --> 00:04:53.990 align:start position:0%
|
| 894 |
+
code will start executing the plan for
|
| 895 |
+
us.<00:04:52.560><c> So</c><00:04:52.720><c> this</c><00:04:52.960><c> will</c><00:04:53.120><c> take</c><00:04:53.280><c> some</c><00:04:53.520><c> time.</c><00:04:53.759><c> Right</c>
|
| 896 |
+
|
| 897 |
+
00:04:53.990 --> 00:04:54.000 align:start position:0%
|
| 898 |
+
us. So this will take some time. Right
|
| 899 |
+
|
| 900 |
+
|
| 901 |
+
00:04:54.000 --> 00:04:56.150 align:start position:0%
|
| 902 |
+
us. So this will take some time. Right
|
| 903 |
+
now<00:04:54.240><c> I'll</c><00:04:54.560><c> just</c><00:04:54.960><c> fast</c><00:04:55.199><c> forward</c><00:04:55.440><c> the</c><00:04:55.680><c> video</c><00:04:55.919><c> and</c>
|
| 904 |
+
|
| 905 |
+
00:04:56.150 --> 00:04:56.160 align:start position:0%
|
| 906 |
+
now I'll just fast forward the video and
|
| 907 |
+
|
| 908 |
+
|
| 909 |
+
00:04:56.160 --> 00:04:58.390 align:start position:0%
|
| 910 |
+
now I'll just fast forward the video and
|
| 911 |
+
this<00:04:56.400><c> will</c><00:04:56.720><c> uh</c><00:04:56.880><c> create</c><00:04:57.199><c> both</c><00:04:57.680><c> font</c><00:04:58.160><c> and</c>
|
| 912 |
+
|
| 913 |
+
00:04:58.390 --> 00:04:58.400 align:start position:0%
|
| 914 |
+
this will uh create both font and
|
| 915 |
+
|
| 916 |
+
|
| 917 |
+
00:04:58.400 --> 00:05:01.189 align:start position:0%
|
| 918 |
+
this will uh create both font and
|
| 919 |
+
backend<00:04:58.800><c> part</c><00:04:59.040><c> for</c><00:04:59.280><c> us.</c><00:04:59.680><c> Okay.</c><00:05:00.560><c> So</c><00:05:00.800><c> here</c><00:05:01.040><c> you</c>
|
| 920 |
+
|
| 921 |
+
00:05:01.189 --> 00:05:01.199 align:start position:0%
|
| 922 |
+
backend part for us. Okay. So here you
|
| 923 |
+
|
| 924 |
+
|
| 925 |
+
00:05:01.199 --> 00:05:03.110 align:start position:0%
|
| 926 |
+
backend part for us. Okay. So here you
|
| 927 |
+
can<00:05:01.360><c> see</c><00:05:01.440><c> the</c><00:05:01.680><c> prompt</c><00:05:02.000><c> is</c><00:05:02.240><c> complete</c><00:05:02.639><c> and</c><00:05:02.880><c> it</c>
|
| 928 |
+
|
| 929 |
+
00:05:03.110 --> 00:05:03.120 align:start position:0%
|
| 930 |
+
can see the prompt is complete and it
|
| 931 |
+
|
| 932 |
+
|
| 933 |
+
00:05:03.120 --> 00:05:06.230 align:start position:0%
|
| 934 |
+
can see the prompt is complete and it
|
| 935 |
+
took<00:05:03.360><c> around</c><00:05:03.759><c> 18</c><00:05:04.160><c> minute</c><00:05:04.479><c> 21</c><00:05:05.120><c> second</c><00:05:05.759><c> and</c><00:05:06.000><c> here</c>
|
| 936 |
+
|
| 937 |
+
00:05:06.230 --> 00:05:06.240 align:start position:0%
|
| 938 |
+
took around 18 minute 21 second and here
|
| 939 |
+
|
| 940 |
+
|
| 941 |
+
00:05:06.240 --> 00:05:08.390 align:start position:0%
|
| 942 |
+
took around 18 minute 21 second and here
|
| 943 |
+
you<00:05:06.479><c> can</c><00:05:06.560><c> see</c><00:05:06.800><c> it</c><00:05:07.039><c> is</c><00:05:07.440><c> uh</c><00:05:07.600><c> telling</c><00:05:07.840><c> us</c><00:05:08.080><c> to</c><00:05:08.240><c> run</c>
|
| 944 |
+
|
| 945 |
+
00:05:08.390 --> 00:05:08.400 align:start position:0%
|
| 946 |
+
you can see it is uh telling us to run
|
| 947 |
+
|
| 948 |
+
|
| 949 |
+
00:05:08.400 --> 00:05:10.390 align:start position:0%
|
| 950 |
+
you can see it is uh telling us to run
|
| 951 |
+
the<00:05:08.639><c> project.</c><00:05:09.440><c> So</c><00:05:09.680><c> it</c><00:05:10.000><c> actually</c>
|
| 952 |
+
|
| 953 |
+
00:05:10.390 --> 00:05:10.400 align:start position:0%
|
| 954 |
+
the project. So it actually
|
| 955 |
+
|
| 956 |
+
|
| 957 |
+
00:05:10.400 --> 00:05:12.710 align:start position:0%
|
| 958 |
+
the project. So it actually
|
| 959 |
+
automatically<00:05:11.440><c> uh</c><00:05:11.600><c> run</c><00:05:11.840><c> those</c><00:05:12.160><c> command</c><00:05:12.479><c> but</c>
|
| 960 |
+
|
| 961 |
+
00:05:12.710 --> 00:05:12.720 align:start position:0%
|
| 962 |
+
automatically uh run those command but
|
| 963 |
+
|
| 964 |
+
|
| 965 |
+
00:05:12.720 --> 00:05:14.310 align:start position:0%
|
| 966 |
+
automatically uh run those command but
|
| 967 |
+
if<00:05:12.880><c> you</c><00:05:12.960><c> want</c><00:05:13.199><c> you</c><00:05:13.360><c> can</c><00:05:13.520><c> run</c><00:05:13.680><c> those</c><00:05:13.919><c> command</c>
|
| 968 |
+
|
| 969 |
+
00:05:14.310 --> 00:05:14.320 align:start position:0%
|
| 970 |
+
if you want you can run those command
|
| 971 |
+
|
| 972 |
+
|
| 973 |
+
00:05:14.320 --> 00:05:16.790 align:start position:0%
|
| 974 |
+
if you want you can run those command
|
| 975 |
+
again<00:05:14.960><c> but</c><00:05:15.199><c> the</c><00:05:15.440><c> only</c><00:05:15.680><c> command</c><00:05:16.160><c> that</c><00:05:16.479><c> we</c><00:05:16.639><c> need</c>
|
| 976 |
+
|
| 977 |
+
00:05:16.790 --> 00:05:16.800 align:start position:0%
|
| 978 |
+
again but the only command that we need
|
| 979 |
+
|
| 980 |
+
|
| 981 |
+
00:05:16.800 --> 00:05:19.510 align:start position:0%
|
| 982 |
+
again but the only command that we need
|
| 983 |
+
to<00:05:16.960><c> run</c><00:05:17.120><c> is</c><00:05:17.360><c> this</c><00:05:17.680><c> one</c><00:05:17.919><c> python</c><00:05:18.400><c> manage.py</c><00:05:19.280><c> run</c>
|
| 984 |
+
|
| 985 |
+
00:05:19.510 --> 00:05:19.520 align:start position:0%
|
| 986 |
+
to run is this one python manage.py run
|
| 987 |
+
|
| 988 |
+
|
| 989 |
+
00:05:19.520 --> 00:05:21.189 align:start position:0%
|
| 990 |
+
to run is this one python manage.py run
|
| 991 |
+
server.<00:05:19.919><c> So</c><00:05:20.080><c> for</c><00:05:20.240><c> it</c><00:05:20.400><c> we</c><00:05:20.560><c> need</c><00:05:20.639><c> to</c><00:05:20.800><c> open</c><00:05:20.960><c> the</c>
|
| 992 |
+
|
| 993 |
+
00:05:21.189 --> 00:05:21.199 align:start position:0%
|
| 994 |
+
server. So for it we need to open the
|
| 995 |
+
|
| 996 |
+
|
| 997 |
+
00:05:21.199 --> 00:05:23.590 align:start position:0%
|
| 998 |
+
server. So for it we need to open the
|
| 999 |
+
command<00:05:21.440><c> prompts</c><00:05:22.160><c> and</c><00:05:22.720><c> uh</c><00:05:22.960><c> here</c><00:05:23.199><c> you</c><00:05:23.360><c> can</c><00:05:23.520><c> see</c>
|
| 1000 |
+
|
| 1001 |
+
00:05:23.590 --> 00:05:23.600 align:start position:0%
|
| 1002 |
+
command prompts and uh here you can see
|
| 1003 |
+
|
| 1004 |
+
|
| 1005 |
+
00:05:23.600 --> 00:05:24.950 align:start position:0%
|
| 1006 |
+
command prompts and uh here you can see
|
| 1007 |
+
terminal.<00:05:24.080><c> So</c><00:05:24.240><c> I'm</c><00:05:24.400><c> going</c><00:05:24.479><c> to</c><00:05:24.560><c> click</c><00:05:24.720><c> on</c>
|
| 1008 |
+
|
| 1009 |
+
00:05:24.950 --> 00:05:24.960 align:start position:0%
|
| 1010 |
+
terminal. So I'm going to click on
|
| 1011 |
+
|
| 1012 |
+
|
| 1013 |
+
00:05:24.960 --> 00:05:27.749 align:start position:0%
|
| 1014 |
+
terminal. So I'm going to click on
|
| 1015 |
+
terminal<00:05:25.440><c> then</c><00:05:25.680><c> click</c><00:05:25.919><c> here</c><00:05:26.240><c> new</c><00:05:26.479><c> terminal</c>
|
| 1016 |
+
|
| 1017 |
+
00:05:27.749 --> 00:05:27.759 align:start position:0%
|
| 1018 |
+
terminal then click here new terminal
|
| 1019 |
+
|
| 1020 |
+
|
| 1021 |
+
00:05:27.759 --> 00:05:29.990 align:start position:0%
|
| 1022 |
+
terminal then click here new terminal
|
| 1023 |
+
and<00:05:28.000><c> here</c><00:05:28.320><c> we</c><00:05:28.479><c> are</c><00:05:28.639><c> going</c><00:05:28.720><c> to</c><00:05:28.960><c> say</c><00:05:29.680><c> we</c><00:05:29.919><c> are</c>
|
| 1024 |
+
|
| 1025 |
+
00:05:29.990 --> 00:05:30.000 align:start position:0%
|
| 1026 |
+
and here we are going to say we are
|
| 1027 |
+
|
| 1028 |
+
|
| 1029 |
+
00:05:30.000 --> 00:05:31.510 align:start position:0%
|
| 1030 |
+
and here we are going to say we are
|
| 1031 |
+
going<00:05:30.080><c> to</c><00:05:30.240><c> write</c><00:05:30.479><c> this</c><00:05:30.720><c> command</c><00:05:31.039><c> python</c>
|
| 1032 |
+
|
| 1033 |
+
00:05:31.510 --> 00:05:31.520 align:start position:0%
|
| 1034 |
+
going to write this command python
|
| 1035 |
+
|
| 1036 |
+
|
| 1037 |
+
00:05:31.520 --> 00:05:34.390 align:start position:0%
|
| 1038 |
+
going to write this command python
|
| 1039 |
+
manage.py<00:05:32.639><c> run</c><00:05:32.960><c> server.</c><00:05:33.520><c> So</c><00:05:33.600><c> I'll</c><00:05:33.840><c> just</c><00:05:34.000><c> copy</c>
|
| 1040 |
+
|
| 1041 |
+
00:05:34.390 --> 00:05:34.400 align:start position:0%
|
| 1042 |
+
manage.py run server. So I'll just copy
|
| 1043 |
+
|
| 1044 |
+
|
| 1045 |
+
00:05:34.400 --> 00:05:37.029 align:start position:0%
|
| 1046 |
+
manage.py run server. So I'll just copy
|
| 1047 |
+
and<00:05:34.800><c> paste</c><00:05:35.039><c> it</c><00:05:35.199><c> over</c><00:05:35.440><c> here</c><00:05:35.759><c> then</c><00:05:36.080><c> press</c><00:05:36.400><c> enter.</c>
|
| 1048 |
+
|
| 1049 |
+
00:05:37.029 --> 00:05:37.039 align:start position:0%
|
| 1050 |
+
and paste it over here then press enter.
|
| 1051 |
+
|
| 1052 |
+
|
| 1053 |
+
00:05:37.039 --> 00:05:39.510 align:start position:0%
|
| 1054 |
+
and paste it over here then press enter.
|
| 1055 |
+
So<00:05:37.199><c> this</c><00:05:37.520><c> will</c><00:05:37.759><c> run</c><00:05:37.919><c> our</c><00:05:38.160><c> project.</c><00:05:39.120><c> So</c><00:05:39.360><c> here</c>
|
| 1056 |
+
|
| 1057 |
+
00:05:39.510 --> 00:05:39.520 align:start position:0%
|
| 1058 |
+
So this will run our project. So here
|
| 1059 |
+
|
| 1060 |
+
|
| 1061 |
+
00:05:39.520 --> 00:05:41.670 align:start position:0%
|
| 1062 |
+
So this will run our project. So here
|
| 1063 |
+
you<00:05:39.759><c> can</c><00:05:39.840><c> see</c><00:05:40.000><c> it</c><00:05:40.240><c> give</c><00:05:40.479><c> us</c><00:05:40.639><c> a</c><00:05:40.800><c> address.</c><00:05:41.360><c> So</c><00:05:41.520><c> I'm</c>
|
| 1064 |
+
|
| 1065 |
+
00:05:41.670 --> 00:05:41.680 align:start position:0%
|
| 1066 |
+
you can see it give us a address. So I'm
|
| 1067 |
+
|
| 1068 |
+
|
| 1069 |
+
00:05:41.680 --> 00:05:43.510 align:start position:0%
|
| 1070 |
+
you can see it give us a address. So I'm
|
| 1071 |
+
just<00:05:41.919><c> going</c><00:05:42.000><c> to</c><00:05:42.160><c> copy</c><00:05:42.479><c> this</c><00:05:42.720><c> address.</c><00:05:43.280><c> Then</c>
|
| 1072 |
+
|
| 1073 |
+
00:05:43.510 --> 00:05:43.520 align:start position:0%
|
| 1074 |
+
just going to copy this address. Then
|
| 1075 |
+
|
| 1076 |
+
|
| 1077 |
+
00:05:43.520 --> 00:05:45.990 align:start position:0%
|
| 1078 |
+
just going to copy this address. Then
|
| 1079 |
+
I'll<00:05:43.840><c> go</c><00:05:43.919><c> to</c><00:05:44.080><c> any</c><00:05:44.320><c> browser</c><00:05:45.199><c> then</c><00:05:45.520><c> paste</c><00:05:45.759><c> it</c>
|
| 1080 |
+
|
| 1081 |
+
00:05:45.990 --> 00:05:46.000 align:start position:0%
|
| 1082 |
+
I'll go to any browser then paste it
|
| 1083 |
+
|
| 1084 |
+
|
| 1085 |
+
00:05:46.000 --> 00:05:48.469 align:start position:0%
|
| 1086 |
+
I'll go to any browser then paste it
|
| 1087 |
+
here<00:05:46.720><c> then</c><00:05:46.960><c> press</c><00:05:47.280><c> enter</c><00:05:47.759><c> and</c><00:05:48.000><c> there</c><00:05:48.160><c> you</c><00:05:48.320><c> can</c>
|
| 1088 |
+
|
| 1089 |
+
00:05:48.469 --> 00:05:48.479 align:start position:0%
|
| 1090 |
+
here then press enter and there you can
|
| 1091 |
+
|
| 1092 |
+
|
| 1093 |
+
00:05:48.479 --> 00:05:51.670 align:start position:0%
|
| 1094 |
+
here then press enter and there you can
|
| 1095 |
+
see<00:05:48.720><c> this</c><00:05:48.960><c> is</c><00:05:49.039><c> our</c><00:05:50.160><c> login</c><00:05:50.639><c> interface.</c><00:05:51.440><c> So</c>
|
| 1096 |
+
|
| 1097 |
+
00:05:51.670 --> 00:05:51.680 align:start position:0%
|
| 1098 |
+
see this is our login interface. So
|
| 1099 |
+
|
| 1100 |
+
|
| 1101 |
+
00:05:51.680 --> 00:05:54.070 align:start position:0%
|
| 1102 |
+
see this is our login interface. So
|
| 1103 |
+
first<00:05:51.840><c> of</c><00:05:52.000><c> all</c><00:05:52.240><c> we</c><00:05:52.479><c> need</c><00:05:52.639><c> to</c><00:05:52.960><c> give</c><00:05:53.199><c> the</c>
|
| 1104 |
+
|
| 1105 |
+
00:05:54.070 --> 00:05:54.080 align:start position:0%
|
| 1106 |
+
first of all we need to give the
|
| 1107 |
+
|
| 1108 |
+
|
| 1109 |
+
00:05:54.080 --> 00:05:56.390 align:start position:0%
|
| 1110 |
+
first of all we need to give the
|
| 1111 |
+
username<00:05:54.560><c> and</c><00:05:54.800><c> password.</c><00:05:55.360><c> So</c><00:05:55.520><c> if</c><00:05:55.759><c> you</c><00:05:55.840><c> face</c><00:05:56.080><c> an</c>
|
| 1112 |
+
|
| 1113 |
+
00:05:56.390 --> 00:05:56.400 align:start position:0%
|
| 1114 |
+
username and password. So if you face an
|
| 1115 |
+
|
| 1116 |
+
|
| 1117 |
+
00:05:56.400 --> 00:05:58.870 align:start position:0%
|
| 1118 |
+
username and password. So if you face an
|
| 1119 |
+
error<00:05:56.720><c> just</c><00:05:56.880><c> copy</c><00:05:57.199><c> the</c><00:05:57.520><c> error</c><00:05:57.759><c> and</c><00:05:58.160><c> paste</c><00:05:58.560><c> it</c>
|
| 1120 |
+
|
| 1121 |
+
00:05:58.870 --> 00:05:58.880 align:start position:0%
|
| 1122 |
+
error just copy the error and paste it
|
| 1123 |
+
|
| 1124 |
+
|
| 1125 |
+
00:05:58.880 --> 00:06:01.749 align:start position:0%
|
| 1126 |
+
error just copy the error and paste it
|
| 1127 |
+
over<00:05:59.280><c> here</c><00:05:59.759><c> and</c><00:06:00.000><c> open</c><00:06:00.400><c> code</c><00:06:00.639><c> will</c><00:06:01.360><c> solve</c><00:06:01.600><c> the</c>
|
| 1128 |
+
|
| 1129 |
+
00:06:01.749 --> 00:06:01.759 align:start position:0%
|
| 1130 |
+
over here and open code will solve the
|
| 1131 |
+
|
| 1132 |
+
|
| 1133 |
+
00:06:01.759 --> 00:06:03.670 align:start position:0%
|
| 1134 |
+
over here and open code will solve the
|
| 1135 |
+
issue<00:06:02.000><c> for</c><00:06:02.160><c> you.</c><00:06:02.400><c> So</c><00:06:02.560><c> here</c><00:06:02.800><c> we're</c><00:06:03.120><c> going</c><00:06:03.280><c> to</c>
|
| 1136 |
+
|
| 1137 |
+
00:06:03.670 --> 00:06:03.680 align:start position:0%
|
| 1138 |
+
issue for you. So here we're going to
|
| 1139 |
+
|
| 1140 |
+
|
| 1141 |
+
00:06:03.680 --> 00:06:06.309 align:start position:0%
|
| 1142 |
+
issue for you. So here we're going to
|
| 1143 |
+
ask<00:06:04.000><c> for</c><00:06:04.160><c> the</c><00:06:04.400><c> admin</c><00:06:04.800><c> username</c><00:06:05.440><c> and</c><00:06:05.759><c> password.</c>
|
| 1144 |
+
|
| 1145 |
+
00:06:06.309 --> 00:06:06.319 align:start position:0%
|
| 1146 |
+
ask for the admin username and password.
|
| 1147 |
+
|
| 1148 |
+
|
| 1149 |
+
00:06:06.319 --> 00:06:09.189 align:start position:0%
|
| 1150 |
+
ask for the admin username and password.
|
| 1151 |
+
So<00:06:06.720><c> I'm</c><00:06:06.960><c> going</c><00:06:07.039><c> to</c><00:06:07.199><c> say</c><00:06:07.360><c> over</c><00:06:07.600><c> here</c><00:06:07.919><c> give</c><00:06:08.160><c> me</c>
|
| 1152 |
+
|
| 1153 |
+
00:06:09.189 --> 00:06:09.199 align:start position:0%
|
| 1154 |
+
So I'm going to say over here give me
|
| 1155 |
+
|
| 1156 |
+
|
| 1157 |
+
00:06:09.199 --> 00:06:12.150 align:start position:0%
|
| 1158 |
+
So I'm going to say over here give me
|
| 1159 |
+
admin<00:06:10.160><c> username</c>
|
| 1160 |
+
|
| 1161 |
+
00:06:12.150 --> 00:06:12.160 align:start position:0%
|
| 1162 |
+
admin username
|
| 1163 |
+
|
| 1164 |
+
|
| 1165 |
+
00:06:12.160 --> 00:06:15.510 align:start position:0%
|
| 1166 |
+
admin username
|
| 1167 |
+
and<00:06:13.199><c> password</c><00:06:14.000><c> then</c><00:06:14.319><c> press</c><00:06:14.639><c> enter.</c><00:06:15.120><c> So</c><00:06:15.280><c> it</c>
|
| 1168 |
+
|
| 1169 |
+
00:06:15.510 --> 00:06:15.520 align:start position:0%
|
| 1170 |
+
and password then press enter. So it
|
| 1171 |
+
|
| 1172 |
+
|
| 1173 |
+
00:06:15.520 --> 00:06:17.189 align:start position:0%
|
| 1174 |
+
and password then press enter. So it
|
| 1175 |
+
will<00:06:15.600><c> give</c><00:06:15.759><c> us</c><00:06:15.919><c> the</c><00:06:16.160><c> admin</c><00:06:16.479><c> username</c><00:06:16.960><c> and</c>
|
| 1176 |
+
|
| 1177 |
+
00:06:17.189 --> 00:06:17.199 align:start position:0%
|
| 1178 |
+
will give us the admin username and
|
| 1179 |
+
|
| 1180 |
+
|
| 1181 |
+
00:06:17.199 --> 00:06:19.510 align:start position:0%
|
| 1182 |
+
will give us the admin username and
|
| 1183 |
+
password<00:06:18.319><c> and</c><00:06:18.720><c> you</c><00:06:18.800><c> can</c><00:06:18.960><c> see</c><00:06:19.120><c> this</c><00:06:19.199><c> is</c><00:06:19.360><c> the</c>
|
| 1184 |
+
|
| 1185 |
+
00:06:19.510 --> 00:06:19.520 align:start position:0%
|
| 1186 |
+
password and you can see this is the
|
| 1187 |
+
|
| 1188 |
+
|
| 1189 |
+
00:06:19.520 --> 00:06:21.590 align:start position:0%
|
| 1190 |
+
password and you can see this is the
|
| 1191 |
+
username.<00:06:20.080><c> Username</c><00:06:20.479><c> is</c><00:06:20.639><c> admin</c><00:06:21.039><c> password</c><00:06:21.440><c> is</c>
|
| 1192 |
+
|
| 1193 |
+
00:06:21.590 --> 00:06:21.600 align:start position:0%
|
| 1194 |
+
username. Username is admin password is
|
| 1195 |
+
|
| 1196 |
+
|
| 1197 |
+
00:06:21.600 --> 00:06:24.550 align:start position:0%
|
| 1198 |
+
username. Username is admin password is
|
| 1199 |
+
admin<00:06:21.919><c> 1</c><00:06:22.080><c> 2</c><00:06:22.240><c> 3.</c><00:06:23.039><c> So</c><00:06:23.199><c> I'm</c><00:06:23.360><c> going</c><00:06:23.520><c> to</c><00:06:23.680><c> s</c><00:06:24.080><c> here</c>
|
| 1200 |
+
|
| 1201 |
+
00:06:24.550 --> 00:06:24.560 align:start position:0%
|
| 1202 |
+
admin 1 2 3. So I'm going to s here
|
| 1203 |
+
|
| 1204 |
+
|
| 1205 |
+
00:06:24.560 --> 00:06:26.469 align:start position:0%
|
| 1206 |
+
admin 1 2 3. So I'm going to s here
|
| 1207 |
+
admin
|
| 1208 |
+
|
| 1209 |
+
00:06:26.469 --> 00:06:26.479 align:start position:0%
|
| 1210 |
+
admin
|
| 1211 |
+
|
| 1212 |
+
|
| 1213 |
+
00:06:26.479 --> 00:06:30.550 align:start position:0%
|
| 1214 |
+
admin
|
| 1215 |
+
and<00:06:26.800><c> password</c><00:06:27.280><c> will</c><00:06:27.520><c> be</c><00:06:28.080><c> admin</c><00:06:29.280><c> 1</c><00:06:29.520><c> 2</c><00:06:29.759><c> 3.</c><00:06:30.319><c> Then</c>
|
| 1216 |
+
|
| 1217 |
+
00:06:30.550 --> 00:06:30.560 align:start position:0%
|
| 1218 |
+
and password will be admin 1 2 3. Then
|
| 1219 |
+
|
| 1220 |
+
|
| 1221 |
+
00:06:30.560 --> 00:06:34.790 align:start position:0%
|
| 1222 |
+
and password will be admin 1 2 3. Then
|
| 1223 |
+
click<00:06:30.800><c> here</c><00:06:31.120><c> sign</c><00:06:31.360><c> in.</c>
|
| 1224 |
+
|
| 1225 |
+
00:06:34.790 --> 00:06:34.800 align:start position:0%
|
| 1226 |
+
|
| 1227 |
+
|
| 1228 |
+
|
| 1229 |
+
00:06:34.800 --> 00:06:36.469 align:start position:0%
|
| 1230 |
+
|
| 1231 |
+
And<00:06:35.039><c> there</c><00:06:35.199><c> you</c><00:06:35.360><c> can</c><00:06:35.520><c> see</c><00:06:35.680><c> it</c><00:06:35.840><c> took</c><00:06:36.000><c> us</c><00:06:36.160><c> to</c><00:06:36.319><c> the</c>
|
| 1232 |
+
|
| 1233 |
+
00:06:36.469 --> 00:06:36.479 align:start position:0%
|
| 1234 |
+
And there you can see it took us to the
|
| 1235 |
+
|
| 1236 |
+
|
| 1237 |
+
00:06:36.479 --> 00:06:38.309 align:start position:0%
|
| 1238 |
+
And there you can see it took us to the
|
| 1239 |
+
admin<00:06:36.800><c> dashboard</c><00:06:37.199><c> and</c><00:06:37.440><c> the</c><00:06:37.680><c> design</c><00:06:37.919><c> is</c>
|
| 1240 |
+
|
| 1241 |
+
00:06:38.309 --> 00:06:38.319 align:start position:0%
|
| 1242 |
+
admin dashboard and the design is
|
| 1243 |
+
|
| 1244 |
+
|
| 1245 |
+
00:06:38.319 --> 00:06:40.870 align:start position:0%
|
| 1246 |
+
admin dashboard and the design is
|
| 1247 |
+
actually<00:06:38.720><c> really</c><00:06:39.120><c> solid</c><00:06:39.520><c> as</c><00:06:39.759><c> you</c><00:06:39.919><c> can</c><00:06:40.080><c> see.</c><00:06:40.720><c> So</c>
|
| 1248 |
+
|
| 1249 |
+
00:06:40.870 --> 00:06:40.880 align:start position:0%
|
| 1250 |
+
actually really solid as you can see. So
|
| 1251 |
+
|
| 1252 |
+
|
| 1253 |
+
00:06:40.880 --> 00:06:43.830 align:start position:0%
|
| 1254 |
+
actually really solid as you can see. So
|
| 1255 |
+
the<00:06:41.199><c> planning</c><00:06:41.680><c> was</c><00:06:42.160><c> created</c><00:06:42.560><c> by</c><00:06:43.120><c> anti</c><00:06:43.520><c> gravity</c>
|
| 1256 |
+
|
| 1257 |
+
00:06:43.830 --> 00:06:43.840 align:start position:0%
|
| 1258 |
+
the planning was created by anti gravity
|
| 1259 |
+
|
| 1260 |
+
|
| 1261 |
+
00:06:43.840 --> 00:06:46.390 align:start position:0%
|
| 1262 |
+
the planning was created by anti gravity
|
| 1263 |
+
and<00:06:44.160><c> designed</c><00:06:44.639><c> us</c><00:06:45.039><c> or</c><00:06:45.360><c> the</c><00:06:45.520><c> code</c><00:06:45.840><c> was</c><00:06:46.080><c> written</c>
|
| 1264 |
+
|
| 1265 |
+
00:06:46.390 --> 00:06:46.400 align:start position:0%
|
| 1266 |
+
and designed us or the code was written
|
| 1267 |
+
|
| 1268 |
+
|
| 1269 |
+
00:06:46.400 --> 00:06:49.110 align:start position:0%
|
| 1270 |
+
and designed us or the code was written
|
| 1271 |
+
by<00:06:46.720><c> open</c><00:06:47.039><c> code</c><00:06:47.280><c> and</c><00:06:47.520><c> the</c><00:06:48.080><c> design</c><00:06:48.400><c> is</c><00:06:48.720><c> actually</c>
|
| 1272 |
+
|
| 1273 |
+
00:06:49.110 --> 00:06:49.120 align:start position:0%
|
| 1274 |
+
by open code and the design is actually
|
| 1275 |
+
|
| 1276 |
+
|
| 1277 |
+
00:06:49.120 --> 00:06:50.309 align:start position:0%
|
| 1278 |
+
by open code and the design is actually
|
| 1279 |
+
really<00:06:49.360><c> good.</c><00:06:49.600><c> There</c><00:06:49.759><c> you</c><00:06:49.919><c> can</c><00:06:50.000><c> see</c><00:06:50.160><c> there</c><00:06:50.240><c> is</c>
|
| 1280 |
+
|
| 1281 |
+
00:06:50.309 --> 00:06:50.319 align:start position:0%
|
| 1282 |
+
really good. There you can see there is
|
| 1283 |
+
|
| 1284 |
+
|
| 1285 |
+
00:06:50.319 --> 00:06:53.510 align:start position:0%
|
| 1286 |
+
really good. There you can see there is
|
| 1287 |
+
some<00:06:50.560><c> hover</c><00:06:50.880><c> effect</c><00:06:51.199><c> over</c><00:06:51.520><c> here</c><00:06:52.880><c> and</c><00:06:53.120><c> the</c>
|
| 1288 |
+
|
| 1289 |
+
00:06:53.510 --> 00:06:53.520 align:start position:0%
|
| 1290 |
+
some hover effect over here and the
|
| 1291 |
+
|
| 1292 |
+
|
| 1293 |
+
00:06:53.520 --> 00:06:56.710 align:start position:0%
|
| 1294 |
+
some hover effect over here and the
|
| 1295 |
+
color<00:06:53.919><c> combination</c><00:06:54.560><c> is</c><00:06:54.880><c> awesome</c>
|
| 1296 |
+
|
| 1297 |
+
00:06:56.710 --> 00:06:56.720 align:start position:0%
|
| 1298 |
+
color combination is awesome
|
| 1299 |
+
|
| 1300 |
+
|
| 1301 |
+
00:06:56.720 --> 00:06:58.870 align:start position:0%
|
| 1302 |
+
color combination is awesome
|
| 1303 |
+
and<00:06:57.039><c> there</c><00:06:57.199><c> is</c><00:06:57.360><c> no</c><00:06:57.680><c> error</c><00:06:57.840><c> over</c><00:06:58.080><c> here.</c><00:06:58.560><c> And</c><00:06:58.720><c> one</c>
|
| 1304 |
+
|
| 1305 |
+
00:06:58.870 --> 00:06:58.880 align:start position:0%
|
| 1306 |
+
and there is no error over here. And one
|
| 1307 |
+
|
| 1308 |
+
|
| 1309 |
+
00:06:58.880 --> 00:07:00.629 align:start position:0%
|
| 1310 |
+
and there is no error over here. And one
|
| 1311 |
+
more<00:06:59.120><c> thing</c><00:06:59.280><c> if</c><00:06:59.440><c> there</c><00:06:59.599><c> is</c><00:06:59.759><c> any</c><00:07:00.080><c> error</c><00:07:00.319><c> you</c><00:07:00.479><c> can</c>
|
| 1312 |
+
|
| 1313 |
+
00:07:00.629 --> 00:07:00.639 align:start position:0%
|
| 1314 |
+
more thing if there is any error you can
|
| 1315 |
+
|
| 1316 |
+
|
| 1317 |
+
00:07:00.639 --> 00:07:03.029 align:start position:0%
|
| 1318 |
+
more thing if there is any error you can
|
| 1319 |
+
just<00:07:00.800><c> copy</c><00:07:01.039><c> the</c><00:07:01.360><c> error</c><00:07:02.000><c> and</c><00:07:02.319><c> give</c><00:07:02.560><c> it</c><00:07:02.720><c> over</c>
|
| 1320 |
+
|
| 1321 |
+
00:07:03.029 --> 00:07:03.039 align:start position:0%
|
| 1322 |
+
just copy the error and give it over
|
| 1323 |
+
|
| 1324 |
+
|
| 1325 |
+
00:07:03.039 --> 00:07:05.350 align:start position:0%
|
| 1326 |
+
just copy the error and give it over
|
| 1327 |
+
here.<00:07:03.360><c> And</c><00:07:03.599><c> if</c><00:07:03.919><c> for</c><00:07:04.240><c> some</c><00:07:04.479><c> reason</c><00:07:05.039><c> uh</c><00:07:05.199><c> let's</c>
|
| 1328 |
+
|
| 1329 |
+
00:07:05.350 --> 00:07:05.360 align:start position:0%
|
| 1330 |
+
here. And if for some reason uh let's
|
| 1331 |
+
|
| 1332 |
+
|
| 1333 |
+
00:07:05.360 --> 00:07:07.350 align:start position:0%
|
| 1334 |
+
here. And if for some reason uh let's
|
| 1335 |
+
say<00:07:05.520><c> you</c><00:07:05.759><c> do</c><00:07:05.840><c> not</c><00:07:06.000><c> like</c><00:07:06.240><c> the</c><00:07:06.479><c> design</c><00:07:06.880><c> by</c><00:07:07.120><c> open</c>
|
| 1336 |
+
|
| 1337 |
+
00:07:07.350 --> 00:07:07.360 align:start position:0%
|
| 1338 |
+
say you do not like the design by open
|
| 1339 |
+
|
| 1340 |
+
|
| 1341 |
+
00:07:07.360 --> 00:07:09.749 align:start position:0%
|
| 1342 |
+
say you do not like the design by open
|
| 1343 |
+
code<00:07:07.599><c> then</c><00:07:07.840><c> you</c><00:07:08.080><c> can</c><00:07:08.160><c> use</c><00:07:08.400><c> anti</c><00:07:09.199><c> to</c><00:07:09.360><c> make</c><00:07:09.520><c> the</c>
|
| 1344 |
+
|
| 1345 |
+
00:07:09.749 --> 00:07:09.759 align:start position:0%
|
| 1346 |
+
code then you can use anti to make the
|
| 1347 |
+
|
| 1348 |
+
|
| 1349 |
+
00:07:09.759 --> 00:07:12.710 align:start position:0%
|
| 1350 |
+
code then you can use anti to make the
|
| 1351 |
+
design.<00:07:10.560><c> As</c><00:07:10.800><c> you</c><00:07:10.960><c> can</c><00:07:11.039><c> see</c><00:07:11.440><c> uh</c><00:07:11.680><c> we</c><00:07:12.000><c> made</c><00:07:12.319><c> the</c>
|
| 1352 |
+
|
| 1353 |
+
00:07:12.710 --> 00:07:12.720 align:start position:0%
|
| 1354 |
+
design. As you can see uh we made the
|
| 1355 |
+
|
| 1356 |
+
|
| 1357 |
+
00:07:12.720 --> 00:07:14.790 align:start position:0%
|
| 1358 |
+
design. As you can see uh we made the
|
| 1359 |
+
complete<00:07:13.120><c> font</c><00:07:13.520><c> and</c><00:07:13.759><c> back</c><00:07:13.919><c> end</c><00:07:14.400><c> all</c><00:07:14.560><c> the</c>
|
| 1360 |
+
|
| 1361 |
+
00:07:14.790 --> 00:07:14.800 align:start position:0%
|
| 1362 |
+
complete font and back end all the
|
| 1363 |
+
|
| 1364 |
+
|
| 1365 |
+
00:07:14.800 --> 00:07:18.309 align:start position:0%
|
| 1366 |
+
complete font and back end all the
|
| 1367 |
+
database<00:07:15.599><c> everything</c><00:07:16.720><c> with</c><00:07:17.039><c> just</c><00:07:17.280><c> a</c><00:07:17.599><c> simple</c>
|
| 1368 |
+
|
| 1369 |
+
00:07:18.309 --> 00:07:18.319 align:start position:0%
|
| 1370 |
+
database everything with just a simple
|
| 1371 |
+
|
| 1372 |
+
|
| 1373 |
+
00:07:18.319 --> 00:07:20.230 align:start position:0%
|
| 1374 |
+
database everything with just a simple
|
| 1375 |
+
single<00:07:18.639><c> command.</c><00:07:19.039><c> We</c><00:07:19.280><c> just</c><00:07:19.520><c> keep</c><00:07:19.680><c> the</c><00:07:19.919><c> command</c>
|
| 1376 |
+
|
| 1377 |
+
00:07:20.230 --> 00:07:20.240 align:start position:0%
|
| 1378 |
+
single command. We just keep the command
|
| 1379 |
+
|
| 1380 |
+
|
| 1381 |
+
00:07:20.240 --> 00:07:22.629 align:start position:0%
|
| 1382 |
+
single command. We just keep the command
|
| 1383 |
+
in<00:07:20.479><c> the</c><00:07:20.639><c> antigavi</c><00:07:21.360><c> antigavity</c><00:07:22.000><c> make</c><00:07:22.160><c> the</c><00:07:22.319><c> plan</c>
|
| 1384 |
+
|
| 1385 |
+
00:07:22.629 --> 00:07:22.639 align:start position:0%
|
| 1386 |
+
in the antigavi antigavity make the plan
|
| 1387 |
+
|
| 1388 |
+
|
| 1389 |
+
00:07:22.639 --> 00:07:25.749 align:start position:0%
|
| 1390 |
+
in the antigavi antigavity make the plan
|
| 1391 |
+
and<00:07:22.880><c> open</c><00:07:23.199><c> code</c><00:07:24.160><c> write</c><00:07:24.400><c> the</c><00:07:24.639><c> code</c><00:07:24.880><c> for</c><00:07:25.039><c> us</c><00:07:25.440><c> and</c>
|
| 1392 |
+
|
| 1393 |
+
00:07:25.749 --> 00:07:25.759 align:start position:0%
|
| 1394 |
+
and open code write the code for us and
|
| 1395 |
+
|
| 1396 |
+
|
| 1397 |
+
00:07:25.759 --> 00:07:28.629 align:start position:0%
|
| 1398 |
+
and open code write the code for us and
|
| 1399 |
+
it<00:07:26.000><c> give</c><00:07:26.240><c> us</c><00:07:26.639><c> a</c><00:07:26.720><c> awesome</c><00:07:27.360><c> website</c><00:07:28.000><c> or</c>
|
| 1400 |
+
|
| 1401 |
+
00:07:28.629 --> 00:07:28.639 align:start position:0%
|
| 1402 |
+
it give us a awesome website or
|
| 1403 |
+
|
| 1404 |
+
|
| 1405 |
+
00:07:28.639 --> 00:07:31.510 align:start position:0%
|
| 1406 |
+
it give us a awesome website or
|
| 1407 |
+
inventory<00:07:29.280><c> system</c><00:07:29.919><c> which</c><00:07:30.240><c> looks</c><00:07:30.639><c> premium</c><00:07:31.120><c> and</c>
|
| 1408 |
+
|
| 1409 |
+
00:07:31.510 --> 00:07:31.520 align:start position:0%
|
| 1410 |
+
inventory system which looks premium and
|
| 1411 |
+
|
| 1412 |
+
|
| 1413 |
+
00:07:31.520 --> 00:07:33.990 align:start position:0%
|
| 1414 |
+
inventory system which looks premium and
|
| 1415 |
+
everything<00:07:31.919><c> working</c><00:07:32.400><c> properly</c><00:07:33.360><c> as</c><00:07:33.599><c> you</c><00:07:33.840><c> can</c>
|
| 1416 |
+
|
| 1417 |
+
00:07:33.990 --> 00:07:34.000 align:start position:0%
|
| 1418 |
+
everything working properly as you can
|
| 1419 |
+
|
| 1420 |
+
|
| 1421 |
+
00:07:34.000 --> 00:07:37.110 align:start position:0%
|
| 1422 |
+
everything working properly as you can
|
| 1423 |
+
see.<00:07:34.639><c> So</c><00:07:34.880><c> yeah</c><00:07:35.680><c> I</c><00:07:36.000><c> hope</c><00:07:36.160><c> you</c><00:07:36.400><c> right</c><00:07:36.639><c> now</c><00:07:36.880><c> know</c>
|
| 1424 |
+
|
| 1425 |
+
00:07:37.110 --> 00:07:37.120 align:start position:0%
|
| 1426 |
+
see. So yeah I hope you right now know
|
| 1427 |
+
|
| 1428 |
+
|
| 1429 |
+
00:07:37.120 --> 00:07:39.990 align:start position:0%
|
| 1430 |
+
see. So yeah I hope you right now know
|
| 1431 |
+
how<00:07:37.360><c> you</c><00:07:37.599><c> can</c><00:07:37.759><c> use</c><00:07:38.080><c> anti</c><00:07:38.880><c> with</c><00:07:39.199><c> open</c><00:07:39.440><c> code</c><00:07:39.759><c> and</c>
|
| 1432 |
+
|
| 1433 |
+
00:07:39.990 --> 00:07:40.000 align:start position:0%
|
| 1434 |
+
how you can use anti with open code and
|
| 1435 |
+
|
| 1436 |
+
|
| 1437 |
+
00:07:40.000 --> 00:07:42.230 align:start position:0%
|
| 1438 |
+
how you can use anti with open code and
|
| 1439 |
+
create<00:07:40.319><c> any</c><00:07:40.560><c> kind</c><00:07:40.720><c> of</c><00:07:40.960><c> project</c><00:07:41.680><c> completely</c>
|
| 1440 |
+
|
| 1441 |
+
00:07:42.230 --> 00:07:42.240 align:start position:0%
|
| 1442 |
+
create any kind of project completely
|
| 1443 |
+
|
| 1444 |
+
|
| 1445 |
+
00:07:42.240 --> 00:07:44.550 align:start position:0%
|
| 1446 |
+
create any kind of project completely
|
| 1447 |
+
for<00:07:42.479><c> free.</c><00:07:42.880><c> I</c><00:07:43.039><c> hope</c><00:07:43.199><c> this</c><00:07:43.440><c> was</c><00:07:43.599><c> useful.</c><00:07:44.080><c> If</c><00:07:44.319><c> you</c>
|
| 1448 |
+
|
| 1449 |
+
00:07:44.550 --> 00:07:44.560 align:start position:0%
|
| 1450 |
+
for free. I hope this was useful. If you
|
| 1451 |
+
|
| 1452 |
+
|
| 1453 |
+
00:07:44.560 --> 00:07:46.629 align:start position:0%
|
| 1454 |
+
for free. I hope this was useful. If you
|
| 1455 |
+
want<00:07:44.800><c> me</c><00:07:44.960><c> to</c><00:07:45.199><c> make</c><00:07:45.360><c> any</c><00:07:45.599><c> kind</c><00:07:45.840><c> of</c><00:07:46.080><c> specific</c>
|
| 1456 |
+
|
| 1457 |
+
00:07:46.629 --> 00:07:46.639 align:start position:0%
|
| 1458 |
+
want me to make any kind of specific
|
| 1459 |
+
|
| 1460 |
+
|
| 1461 |
+
00:07:46.639 --> 00:07:48.790 align:start position:0%
|
| 1462 |
+
want me to make any kind of specific
|
| 1463 |
+
video<00:07:47.039><c> related</c><00:07:47.440><c> to</c><00:07:47.599><c> AI,</c><00:07:48.000><c> just</c><00:07:48.240><c> let</c><00:07:48.400><c> me</c><00:07:48.560><c> know</c><00:07:48.639><c> in</c>
|
| 1464 |
+
|
| 1465 |
+
00:07:48.790 --> 00:07:48.800 align:start position:0%
|
| 1466 |
+
video related to AI, just let me know in
|
| 1467 |
+
|
| 1468 |
+
|
| 1469 |
+
00:07:48.800 --> 00:07:51.680 align:start position:0%
|
| 1470 |
+
video related to AI, just let me know in
|
| 1471 |
+
the<00:07:48.960><c> comment</c><00:07:49.199><c> section.</c>
|
| 1472 |
+
|
package-lock.json
ADDED
|
@@ -0,0 +1,1078 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "agent-bridge",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "agent-bridge",
|
| 9 |
+
"version": "1.0.0",
|
| 10 |
+
"license": "MIT",
|
| 11 |
+
"dependencies": {
|
| 12 |
+
"cors": "^2.8.5",
|
| 13 |
+
"express": "^4.21.0",
|
| 14 |
+
"socket.io": "^4.7.5"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"node_modules/@socket.io/component-emitter": {
|
| 18 |
+
"version": "3.1.2",
|
| 19 |
+
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
| 20 |
+
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
| 21 |
+
"license": "MIT"
|
| 22 |
+
},
|
| 23 |
+
"node_modules/@types/cors": {
|
| 24 |
+
"version": "2.8.19",
|
| 25 |
+
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
|
| 26 |
+
"integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
|
| 27 |
+
"license": "MIT",
|
| 28 |
+
"dependencies": {
|
| 29 |
+
"@types/node": "*"
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
"node_modules/@types/node": {
|
| 33 |
+
"version": "25.3.3",
|
| 34 |
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.3.tgz",
|
| 35 |
+
"integrity": "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==",
|
| 36 |
+
"license": "MIT",
|
| 37 |
+
"dependencies": {
|
| 38 |
+
"undici-types": "~7.18.0"
|
| 39 |
+
}
|
| 40 |
+
},
|
| 41 |
+
"node_modules/accepts": {
|
| 42 |
+
"version": "1.3.8",
|
| 43 |
+
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
| 44 |
+
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
| 45 |
+
"license": "MIT",
|
| 46 |
+
"dependencies": {
|
| 47 |
+
"mime-types": "~2.1.34",
|
| 48 |
+
"negotiator": "0.6.3"
|
| 49 |
+
},
|
| 50 |
+
"engines": {
|
| 51 |
+
"node": ">= 0.6"
|
| 52 |
+
}
|
| 53 |
+
},
|
| 54 |
+
"node_modules/array-flatten": {
|
| 55 |
+
"version": "1.1.1",
|
| 56 |
+
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
| 57 |
+
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
| 58 |
+
"license": "MIT"
|
| 59 |
+
},
|
| 60 |
+
"node_modules/base64id": {
|
| 61 |
+
"version": "2.0.0",
|
| 62 |
+
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
| 63 |
+
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
|
| 64 |
+
"license": "MIT",
|
| 65 |
+
"engines": {
|
| 66 |
+
"node": "^4.5.0 || >= 5.9"
|
| 67 |
+
}
|
| 68 |
+
},
|
| 69 |
+
"node_modules/body-parser": {
|
| 70 |
+
"version": "1.20.4",
|
| 71 |
+
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
|
| 72 |
+
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
|
| 73 |
+
"license": "MIT",
|
| 74 |
+
"dependencies": {
|
| 75 |
+
"bytes": "~3.1.2",
|
| 76 |
+
"content-type": "~1.0.5",
|
| 77 |
+
"debug": "2.6.9",
|
| 78 |
+
"depd": "2.0.0",
|
| 79 |
+
"destroy": "~1.2.0",
|
| 80 |
+
"http-errors": "~2.0.1",
|
| 81 |
+
"iconv-lite": "~0.4.24",
|
| 82 |
+
"on-finished": "~2.4.1",
|
| 83 |
+
"qs": "~6.14.0",
|
| 84 |
+
"raw-body": "~2.5.3",
|
| 85 |
+
"type-is": "~1.6.18",
|
| 86 |
+
"unpipe": "~1.0.0"
|
| 87 |
+
},
|
| 88 |
+
"engines": {
|
| 89 |
+
"node": ">= 0.8",
|
| 90 |
+
"npm": "1.2.8000 || >= 1.4.16"
|
| 91 |
+
}
|
| 92 |
+
},
|
| 93 |
+
"node_modules/bytes": {
|
| 94 |
+
"version": "3.1.2",
|
| 95 |
+
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
| 96 |
+
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
| 97 |
+
"license": "MIT",
|
| 98 |
+
"engines": {
|
| 99 |
+
"node": ">= 0.8"
|
| 100 |
+
}
|
| 101 |
+
},
|
| 102 |
+
"node_modules/call-bind-apply-helpers": {
|
| 103 |
+
"version": "1.0.2",
|
| 104 |
+
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
| 105 |
+
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
| 106 |
+
"license": "MIT",
|
| 107 |
+
"dependencies": {
|
| 108 |
+
"es-errors": "^1.3.0",
|
| 109 |
+
"function-bind": "^1.1.2"
|
| 110 |
+
},
|
| 111 |
+
"engines": {
|
| 112 |
+
"node": ">= 0.4"
|
| 113 |
+
}
|
| 114 |
+
},
|
| 115 |
+
"node_modules/call-bound": {
|
| 116 |
+
"version": "1.0.4",
|
| 117 |
+
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
| 118 |
+
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
| 119 |
+
"license": "MIT",
|
| 120 |
+
"dependencies": {
|
| 121 |
+
"call-bind-apply-helpers": "^1.0.2",
|
| 122 |
+
"get-intrinsic": "^1.3.0"
|
| 123 |
+
},
|
| 124 |
+
"engines": {
|
| 125 |
+
"node": ">= 0.4"
|
| 126 |
+
},
|
| 127 |
+
"funding": {
|
| 128 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 129 |
+
}
|
| 130 |
+
},
|
| 131 |
+
"node_modules/content-disposition": {
|
| 132 |
+
"version": "0.5.4",
|
| 133 |
+
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
| 134 |
+
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
| 135 |
+
"license": "MIT",
|
| 136 |
+
"dependencies": {
|
| 137 |
+
"safe-buffer": "5.2.1"
|
| 138 |
+
},
|
| 139 |
+
"engines": {
|
| 140 |
+
"node": ">= 0.6"
|
| 141 |
+
}
|
| 142 |
+
},
|
| 143 |
+
"node_modules/content-type": {
|
| 144 |
+
"version": "1.0.5",
|
| 145 |
+
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
| 146 |
+
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
| 147 |
+
"license": "MIT",
|
| 148 |
+
"engines": {
|
| 149 |
+
"node": ">= 0.6"
|
| 150 |
+
}
|
| 151 |
+
},
|
| 152 |
+
"node_modules/cookie": {
|
| 153 |
+
"version": "0.7.2",
|
| 154 |
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
| 155 |
+
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
| 156 |
+
"license": "MIT",
|
| 157 |
+
"engines": {
|
| 158 |
+
"node": ">= 0.6"
|
| 159 |
+
}
|
| 160 |
+
},
|
| 161 |
+
"node_modules/cookie-signature": {
|
| 162 |
+
"version": "1.0.7",
|
| 163 |
+
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
|
| 164 |
+
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
|
| 165 |
+
"license": "MIT"
|
| 166 |
+
},
|
| 167 |
+
"node_modules/cors": {
|
| 168 |
+
"version": "2.8.6",
|
| 169 |
+
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
|
| 170 |
+
"integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
|
| 171 |
+
"license": "MIT",
|
| 172 |
+
"dependencies": {
|
| 173 |
+
"object-assign": "^4",
|
| 174 |
+
"vary": "^1"
|
| 175 |
+
},
|
| 176 |
+
"engines": {
|
| 177 |
+
"node": ">= 0.10"
|
| 178 |
+
},
|
| 179 |
+
"funding": {
|
| 180 |
+
"type": "opencollective",
|
| 181 |
+
"url": "https://opencollective.com/express"
|
| 182 |
+
}
|
| 183 |
+
},
|
| 184 |
+
"node_modules/debug": {
|
| 185 |
+
"version": "2.6.9",
|
| 186 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
| 187 |
+
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
| 188 |
+
"license": "MIT",
|
| 189 |
+
"dependencies": {
|
| 190 |
+
"ms": "2.0.0"
|
| 191 |
+
}
|
| 192 |
+
},
|
| 193 |
+
"node_modules/depd": {
|
| 194 |
+
"version": "2.0.0",
|
| 195 |
+
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
| 196 |
+
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
| 197 |
+
"license": "MIT",
|
| 198 |
+
"engines": {
|
| 199 |
+
"node": ">= 0.8"
|
| 200 |
+
}
|
| 201 |
+
},
|
| 202 |
+
"node_modules/destroy": {
|
| 203 |
+
"version": "1.2.0",
|
| 204 |
+
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
| 205 |
+
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
| 206 |
+
"license": "MIT",
|
| 207 |
+
"engines": {
|
| 208 |
+
"node": ">= 0.8",
|
| 209 |
+
"npm": "1.2.8000 || >= 1.4.16"
|
| 210 |
+
}
|
| 211 |
+
},
|
| 212 |
+
"node_modules/dunder-proto": {
|
| 213 |
+
"version": "1.0.1",
|
| 214 |
+
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
| 215 |
+
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
| 216 |
+
"license": "MIT",
|
| 217 |
+
"dependencies": {
|
| 218 |
+
"call-bind-apply-helpers": "^1.0.1",
|
| 219 |
+
"es-errors": "^1.3.0",
|
| 220 |
+
"gopd": "^1.2.0"
|
| 221 |
+
},
|
| 222 |
+
"engines": {
|
| 223 |
+
"node": ">= 0.4"
|
| 224 |
+
}
|
| 225 |
+
},
|
| 226 |
+
"node_modules/ee-first": {
|
| 227 |
+
"version": "1.1.1",
|
| 228 |
+
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
| 229 |
+
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
| 230 |
+
"license": "MIT"
|
| 231 |
+
},
|
| 232 |
+
"node_modules/encodeurl": {
|
| 233 |
+
"version": "2.0.0",
|
| 234 |
+
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
| 235 |
+
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
| 236 |
+
"license": "MIT",
|
| 237 |
+
"engines": {
|
| 238 |
+
"node": ">= 0.8"
|
| 239 |
+
}
|
| 240 |
+
},
|
| 241 |
+
"node_modules/engine.io": {
|
| 242 |
+
"version": "6.6.5",
|
| 243 |
+
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz",
|
| 244 |
+
"integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==",
|
| 245 |
+
"license": "MIT",
|
| 246 |
+
"dependencies": {
|
| 247 |
+
"@types/cors": "^2.8.12",
|
| 248 |
+
"@types/node": ">=10.0.0",
|
| 249 |
+
"accepts": "~1.3.4",
|
| 250 |
+
"base64id": "2.0.0",
|
| 251 |
+
"cookie": "~0.7.2",
|
| 252 |
+
"cors": "~2.8.5",
|
| 253 |
+
"debug": "~4.4.1",
|
| 254 |
+
"engine.io-parser": "~5.2.1",
|
| 255 |
+
"ws": "~8.18.3"
|
| 256 |
+
},
|
| 257 |
+
"engines": {
|
| 258 |
+
"node": ">=10.2.0"
|
| 259 |
+
}
|
| 260 |
+
},
|
| 261 |
+
"node_modules/engine.io-parser": {
|
| 262 |
+
"version": "5.2.3",
|
| 263 |
+
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
| 264 |
+
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
| 265 |
+
"license": "MIT",
|
| 266 |
+
"engines": {
|
| 267 |
+
"node": ">=10.0.0"
|
| 268 |
+
}
|
| 269 |
+
},
|
| 270 |
+
"node_modules/engine.io/node_modules/debug": {
|
| 271 |
+
"version": "4.4.3",
|
| 272 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
| 273 |
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
| 274 |
+
"license": "MIT",
|
| 275 |
+
"dependencies": {
|
| 276 |
+
"ms": "^2.1.3"
|
| 277 |
+
},
|
| 278 |
+
"engines": {
|
| 279 |
+
"node": ">=6.0"
|
| 280 |
+
},
|
| 281 |
+
"peerDependenciesMeta": {
|
| 282 |
+
"supports-color": {
|
| 283 |
+
"optional": true
|
| 284 |
+
}
|
| 285 |
+
}
|
| 286 |
+
},
|
| 287 |
+
"node_modules/engine.io/node_modules/ms": {
|
| 288 |
+
"version": "2.1.3",
|
| 289 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 290 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 291 |
+
"license": "MIT"
|
| 292 |
+
},
|
| 293 |
+
"node_modules/es-define-property": {
|
| 294 |
+
"version": "1.0.1",
|
| 295 |
+
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
| 296 |
+
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
| 297 |
+
"license": "MIT",
|
| 298 |
+
"engines": {
|
| 299 |
+
"node": ">= 0.4"
|
| 300 |
+
}
|
| 301 |
+
},
|
| 302 |
+
"node_modules/es-errors": {
|
| 303 |
+
"version": "1.3.0",
|
| 304 |
+
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
| 305 |
+
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
| 306 |
+
"license": "MIT",
|
| 307 |
+
"engines": {
|
| 308 |
+
"node": ">= 0.4"
|
| 309 |
+
}
|
| 310 |
+
},
|
| 311 |
+
"node_modules/es-object-atoms": {
|
| 312 |
+
"version": "1.1.1",
|
| 313 |
+
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
| 314 |
+
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
| 315 |
+
"license": "MIT",
|
| 316 |
+
"dependencies": {
|
| 317 |
+
"es-errors": "^1.3.0"
|
| 318 |
+
},
|
| 319 |
+
"engines": {
|
| 320 |
+
"node": ">= 0.4"
|
| 321 |
+
}
|
| 322 |
+
},
|
| 323 |
+
"node_modules/escape-html": {
|
| 324 |
+
"version": "1.0.3",
|
| 325 |
+
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
| 326 |
+
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
| 327 |
+
"license": "MIT"
|
| 328 |
+
},
|
| 329 |
+
"node_modules/etag": {
|
| 330 |
+
"version": "1.8.1",
|
| 331 |
+
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
| 332 |
+
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
| 333 |
+
"license": "MIT",
|
| 334 |
+
"engines": {
|
| 335 |
+
"node": ">= 0.6"
|
| 336 |
+
}
|
| 337 |
+
},
|
| 338 |
+
"node_modules/express": {
|
| 339 |
+
"version": "4.22.1",
|
| 340 |
+
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
| 341 |
+
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
| 342 |
+
"license": "MIT",
|
| 343 |
+
"dependencies": {
|
| 344 |
+
"accepts": "~1.3.8",
|
| 345 |
+
"array-flatten": "1.1.1",
|
| 346 |
+
"body-parser": "~1.20.3",
|
| 347 |
+
"content-disposition": "~0.5.4",
|
| 348 |
+
"content-type": "~1.0.4",
|
| 349 |
+
"cookie": "~0.7.1",
|
| 350 |
+
"cookie-signature": "~1.0.6",
|
| 351 |
+
"debug": "2.6.9",
|
| 352 |
+
"depd": "2.0.0",
|
| 353 |
+
"encodeurl": "~2.0.0",
|
| 354 |
+
"escape-html": "~1.0.3",
|
| 355 |
+
"etag": "~1.8.1",
|
| 356 |
+
"finalhandler": "~1.3.1",
|
| 357 |
+
"fresh": "~0.5.2",
|
| 358 |
+
"http-errors": "~2.0.0",
|
| 359 |
+
"merge-descriptors": "1.0.3",
|
| 360 |
+
"methods": "~1.1.2",
|
| 361 |
+
"on-finished": "~2.4.1",
|
| 362 |
+
"parseurl": "~1.3.3",
|
| 363 |
+
"path-to-regexp": "~0.1.12",
|
| 364 |
+
"proxy-addr": "~2.0.7",
|
| 365 |
+
"qs": "~6.14.0",
|
| 366 |
+
"range-parser": "~1.2.1",
|
| 367 |
+
"safe-buffer": "5.2.1",
|
| 368 |
+
"send": "~0.19.0",
|
| 369 |
+
"serve-static": "~1.16.2",
|
| 370 |
+
"setprototypeof": "1.2.0",
|
| 371 |
+
"statuses": "~2.0.1",
|
| 372 |
+
"type-is": "~1.6.18",
|
| 373 |
+
"utils-merge": "1.0.1",
|
| 374 |
+
"vary": "~1.1.2"
|
| 375 |
+
},
|
| 376 |
+
"engines": {
|
| 377 |
+
"node": ">= 0.10.0"
|
| 378 |
+
},
|
| 379 |
+
"funding": {
|
| 380 |
+
"type": "opencollective",
|
| 381 |
+
"url": "https://opencollective.com/express"
|
| 382 |
+
}
|
| 383 |
+
},
|
| 384 |
+
"node_modules/finalhandler": {
|
| 385 |
+
"version": "1.3.2",
|
| 386 |
+
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
|
| 387 |
+
"integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
|
| 388 |
+
"license": "MIT",
|
| 389 |
+
"dependencies": {
|
| 390 |
+
"debug": "2.6.9",
|
| 391 |
+
"encodeurl": "~2.0.0",
|
| 392 |
+
"escape-html": "~1.0.3",
|
| 393 |
+
"on-finished": "~2.4.1",
|
| 394 |
+
"parseurl": "~1.3.3",
|
| 395 |
+
"statuses": "~2.0.2",
|
| 396 |
+
"unpipe": "~1.0.0"
|
| 397 |
+
},
|
| 398 |
+
"engines": {
|
| 399 |
+
"node": ">= 0.8"
|
| 400 |
+
}
|
| 401 |
+
},
|
| 402 |
+
"node_modules/forwarded": {
|
| 403 |
+
"version": "0.2.0",
|
| 404 |
+
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
| 405 |
+
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
| 406 |
+
"license": "MIT",
|
| 407 |
+
"engines": {
|
| 408 |
+
"node": ">= 0.6"
|
| 409 |
+
}
|
| 410 |
+
},
|
| 411 |
+
"node_modules/fresh": {
|
| 412 |
+
"version": "0.5.2",
|
| 413 |
+
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
| 414 |
+
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
| 415 |
+
"license": "MIT",
|
| 416 |
+
"engines": {
|
| 417 |
+
"node": ">= 0.6"
|
| 418 |
+
}
|
| 419 |
+
},
|
| 420 |
+
"node_modules/function-bind": {
|
| 421 |
+
"version": "1.1.2",
|
| 422 |
+
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
| 423 |
+
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
| 424 |
+
"license": "MIT",
|
| 425 |
+
"funding": {
|
| 426 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 427 |
+
}
|
| 428 |
+
},
|
| 429 |
+
"node_modules/get-intrinsic": {
|
| 430 |
+
"version": "1.3.0",
|
| 431 |
+
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
| 432 |
+
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
| 433 |
+
"license": "MIT",
|
| 434 |
+
"dependencies": {
|
| 435 |
+
"call-bind-apply-helpers": "^1.0.2",
|
| 436 |
+
"es-define-property": "^1.0.1",
|
| 437 |
+
"es-errors": "^1.3.0",
|
| 438 |
+
"es-object-atoms": "^1.1.1",
|
| 439 |
+
"function-bind": "^1.1.2",
|
| 440 |
+
"get-proto": "^1.0.1",
|
| 441 |
+
"gopd": "^1.2.0",
|
| 442 |
+
"has-symbols": "^1.1.0",
|
| 443 |
+
"hasown": "^2.0.2",
|
| 444 |
+
"math-intrinsics": "^1.1.0"
|
| 445 |
+
},
|
| 446 |
+
"engines": {
|
| 447 |
+
"node": ">= 0.4"
|
| 448 |
+
},
|
| 449 |
+
"funding": {
|
| 450 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 451 |
+
}
|
| 452 |
+
},
|
| 453 |
+
"node_modules/get-proto": {
|
| 454 |
+
"version": "1.0.1",
|
| 455 |
+
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
| 456 |
+
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
| 457 |
+
"license": "MIT",
|
| 458 |
+
"dependencies": {
|
| 459 |
+
"dunder-proto": "^1.0.1",
|
| 460 |
+
"es-object-atoms": "^1.0.0"
|
| 461 |
+
},
|
| 462 |
+
"engines": {
|
| 463 |
+
"node": ">= 0.4"
|
| 464 |
+
}
|
| 465 |
+
},
|
| 466 |
+
"node_modules/gopd": {
|
| 467 |
+
"version": "1.2.0",
|
| 468 |
+
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
| 469 |
+
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
| 470 |
+
"license": "MIT",
|
| 471 |
+
"engines": {
|
| 472 |
+
"node": ">= 0.4"
|
| 473 |
+
},
|
| 474 |
+
"funding": {
|
| 475 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 476 |
+
}
|
| 477 |
+
},
|
| 478 |
+
"node_modules/has-symbols": {
|
| 479 |
+
"version": "1.1.0",
|
| 480 |
+
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
| 481 |
+
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
| 482 |
+
"license": "MIT",
|
| 483 |
+
"engines": {
|
| 484 |
+
"node": ">= 0.4"
|
| 485 |
+
},
|
| 486 |
+
"funding": {
|
| 487 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 488 |
+
}
|
| 489 |
+
},
|
| 490 |
+
"node_modules/hasown": {
|
| 491 |
+
"version": "2.0.2",
|
| 492 |
+
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
| 493 |
+
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
| 494 |
+
"license": "MIT",
|
| 495 |
+
"dependencies": {
|
| 496 |
+
"function-bind": "^1.1.2"
|
| 497 |
+
},
|
| 498 |
+
"engines": {
|
| 499 |
+
"node": ">= 0.4"
|
| 500 |
+
}
|
| 501 |
+
},
|
| 502 |
+
"node_modules/http-errors": {
|
| 503 |
+
"version": "2.0.1",
|
| 504 |
+
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
| 505 |
+
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
| 506 |
+
"license": "MIT",
|
| 507 |
+
"dependencies": {
|
| 508 |
+
"depd": "~2.0.0",
|
| 509 |
+
"inherits": "~2.0.4",
|
| 510 |
+
"setprototypeof": "~1.2.0",
|
| 511 |
+
"statuses": "~2.0.2",
|
| 512 |
+
"toidentifier": "~1.0.1"
|
| 513 |
+
},
|
| 514 |
+
"engines": {
|
| 515 |
+
"node": ">= 0.8"
|
| 516 |
+
},
|
| 517 |
+
"funding": {
|
| 518 |
+
"type": "opencollective",
|
| 519 |
+
"url": "https://opencollective.com/express"
|
| 520 |
+
}
|
| 521 |
+
},
|
| 522 |
+
"node_modules/iconv-lite": {
|
| 523 |
+
"version": "0.4.24",
|
| 524 |
+
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
| 525 |
+
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
| 526 |
+
"license": "MIT",
|
| 527 |
+
"dependencies": {
|
| 528 |
+
"safer-buffer": ">= 2.1.2 < 3"
|
| 529 |
+
},
|
| 530 |
+
"engines": {
|
| 531 |
+
"node": ">=0.10.0"
|
| 532 |
+
}
|
| 533 |
+
},
|
| 534 |
+
"node_modules/inherits": {
|
| 535 |
+
"version": "2.0.4",
|
| 536 |
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
| 537 |
+
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
| 538 |
+
"license": "ISC"
|
| 539 |
+
},
|
| 540 |
+
"node_modules/ipaddr.js": {
|
| 541 |
+
"version": "1.9.1",
|
| 542 |
+
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
| 543 |
+
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
| 544 |
+
"license": "MIT",
|
| 545 |
+
"engines": {
|
| 546 |
+
"node": ">= 0.10"
|
| 547 |
+
}
|
| 548 |
+
},
|
| 549 |
+
"node_modules/math-intrinsics": {
|
| 550 |
+
"version": "1.1.0",
|
| 551 |
+
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
| 552 |
+
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
| 553 |
+
"license": "MIT",
|
| 554 |
+
"engines": {
|
| 555 |
+
"node": ">= 0.4"
|
| 556 |
+
}
|
| 557 |
+
},
|
| 558 |
+
"node_modules/media-typer": {
|
| 559 |
+
"version": "0.3.0",
|
| 560 |
+
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
| 561 |
+
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
| 562 |
+
"license": "MIT",
|
| 563 |
+
"engines": {
|
| 564 |
+
"node": ">= 0.6"
|
| 565 |
+
}
|
| 566 |
+
},
|
| 567 |
+
"node_modules/merge-descriptors": {
|
| 568 |
+
"version": "1.0.3",
|
| 569 |
+
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
| 570 |
+
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
| 571 |
+
"license": "MIT",
|
| 572 |
+
"funding": {
|
| 573 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 574 |
+
}
|
| 575 |
+
},
|
| 576 |
+
"node_modules/methods": {
|
| 577 |
+
"version": "1.1.2",
|
| 578 |
+
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
| 579 |
+
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
| 580 |
+
"license": "MIT",
|
| 581 |
+
"engines": {
|
| 582 |
+
"node": ">= 0.6"
|
| 583 |
+
}
|
| 584 |
+
},
|
| 585 |
+
"node_modules/mime": {
|
| 586 |
+
"version": "1.6.0",
|
| 587 |
+
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
| 588 |
+
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
| 589 |
+
"license": "MIT",
|
| 590 |
+
"bin": {
|
| 591 |
+
"mime": "cli.js"
|
| 592 |
+
},
|
| 593 |
+
"engines": {
|
| 594 |
+
"node": ">=4"
|
| 595 |
+
}
|
| 596 |
+
},
|
| 597 |
+
"node_modules/mime-db": {
|
| 598 |
+
"version": "1.52.0",
|
| 599 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
| 600 |
+
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
| 601 |
+
"license": "MIT",
|
| 602 |
+
"engines": {
|
| 603 |
+
"node": ">= 0.6"
|
| 604 |
+
}
|
| 605 |
+
},
|
| 606 |
+
"node_modules/mime-types": {
|
| 607 |
+
"version": "2.1.35",
|
| 608 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
| 609 |
+
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
| 610 |
+
"license": "MIT",
|
| 611 |
+
"dependencies": {
|
| 612 |
+
"mime-db": "1.52.0"
|
| 613 |
+
},
|
| 614 |
+
"engines": {
|
| 615 |
+
"node": ">= 0.6"
|
| 616 |
+
}
|
| 617 |
+
},
|
| 618 |
+
"node_modules/ms": {
|
| 619 |
+
"version": "2.0.0",
|
| 620 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
| 621 |
+
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
| 622 |
+
"license": "MIT"
|
| 623 |
+
},
|
| 624 |
+
"node_modules/negotiator": {
|
| 625 |
+
"version": "0.6.3",
|
| 626 |
+
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
| 627 |
+
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
| 628 |
+
"license": "MIT",
|
| 629 |
+
"engines": {
|
| 630 |
+
"node": ">= 0.6"
|
| 631 |
+
}
|
| 632 |
+
},
|
| 633 |
+
"node_modules/object-assign": {
|
| 634 |
+
"version": "4.1.1",
|
| 635 |
+
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
| 636 |
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
| 637 |
+
"license": "MIT",
|
| 638 |
+
"engines": {
|
| 639 |
+
"node": ">=0.10.0"
|
| 640 |
+
}
|
| 641 |
+
},
|
| 642 |
+
"node_modules/object-inspect": {
|
| 643 |
+
"version": "1.13.4",
|
| 644 |
+
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
| 645 |
+
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
| 646 |
+
"license": "MIT",
|
| 647 |
+
"engines": {
|
| 648 |
+
"node": ">= 0.4"
|
| 649 |
+
},
|
| 650 |
+
"funding": {
|
| 651 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 652 |
+
}
|
| 653 |
+
},
|
| 654 |
+
"node_modules/on-finished": {
|
| 655 |
+
"version": "2.4.1",
|
| 656 |
+
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
| 657 |
+
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
| 658 |
+
"license": "MIT",
|
| 659 |
+
"dependencies": {
|
| 660 |
+
"ee-first": "1.1.1"
|
| 661 |
+
},
|
| 662 |
+
"engines": {
|
| 663 |
+
"node": ">= 0.8"
|
| 664 |
+
}
|
| 665 |
+
},
|
| 666 |
+
"node_modules/parseurl": {
|
| 667 |
+
"version": "1.3.3",
|
| 668 |
+
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
| 669 |
+
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
| 670 |
+
"license": "MIT",
|
| 671 |
+
"engines": {
|
| 672 |
+
"node": ">= 0.8"
|
| 673 |
+
}
|
| 674 |
+
},
|
| 675 |
+
"node_modules/path-to-regexp": {
|
| 676 |
+
"version": "0.1.12",
|
| 677 |
+
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
| 678 |
+
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
| 679 |
+
"license": "MIT"
|
| 680 |
+
},
|
| 681 |
+
"node_modules/proxy-addr": {
|
| 682 |
+
"version": "2.0.7",
|
| 683 |
+
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
| 684 |
+
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
| 685 |
+
"license": "MIT",
|
| 686 |
+
"dependencies": {
|
| 687 |
+
"forwarded": "0.2.0",
|
| 688 |
+
"ipaddr.js": "1.9.1"
|
| 689 |
+
},
|
| 690 |
+
"engines": {
|
| 691 |
+
"node": ">= 0.10"
|
| 692 |
+
}
|
| 693 |
+
},
|
| 694 |
+
"node_modules/qs": {
|
| 695 |
+
"version": "6.14.2",
|
| 696 |
+
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
|
| 697 |
+
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
|
| 698 |
+
"license": "BSD-3-Clause",
|
| 699 |
+
"dependencies": {
|
| 700 |
+
"side-channel": "^1.1.0"
|
| 701 |
+
},
|
| 702 |
+
"engines": {
|
| 703 |
+
"node": ">=0.6"
|
| 704 |
+
},
|
| 705 |
+
"funding": {
|
| 706 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 707 |
+
}
|
| 708 |
+
},
|
| 709 |
+
"node_modules/range-parser": {
|
| 710 |
+
"version": "1.2.1",
|
| 711 |
+
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
| 712 |
+
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
| 713 |
+
"license": "MIT",
|
| 714 |
+
"engines": {
|
| 715 |
+
"node": ">= 0.6"
|
| 716 |
+
}
|
| 717 |
+
},
|
| 718 |
+
"node_modules/raw-body": {
|
| 719 |
+
"version": "2.5.3",
|
| 720 |
+
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
|
| 721 |
+
"integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
|
| 722 |
+
"license": "MIT",
|
| 723 |
+
"dependencies": {
|
| 724 |
+
"bytes": "~3.1.2",
|
| 725 |
+
"http-errors": "~2.0.1",
|
| 726 |
+
"iconv-lite": "~0.4.24",
|
| 727 |
+
"unpipe": "~1.0.0"
|
| 728 |
+
},
|
| 729 |
+
"engines": {
|
| 730 |
+
"node": ">= 0.8"
|
| 731 |
+
}
|
| 732 |
+
},
|
| 733 |
+
"node_modules/safe-buffer": {
|
| 734 |
+
"version": "5.2.1",
|
| 735 |
+
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
| 736 |
+
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
| 737 |
+
"funding": [
|
| 738 |
+
{
|
| 739 |
+
"type": "github",
|
| 740 |
+
"url": "https://github.com/sponsors/feross"
|
| 741 |
+
},
|
| 742 |
+
{
|
| 743 |
+
"type": "patreon",
|
| 744 |
+
"url": "https://www.patreon.com/feross"
|
| 745 |
+
},
|
| 746 |
+
{
|
| 747 |
+
"type": "consulting",
|
| 748 |
+
"url": "https://feross.org/support"
|
| 749 |
+
}
|
| 750 |
+
],
|
| 751 |
+
"license": "MIT"
|
| 752 |
+
},
|
| 753 |
+
"node_modules/safer-buffer": {
|
| 754 |
+
"version": "2.1.2",
|
| 755 |
+
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
| 756 |
+
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
| 757 |
+
"license": "MIT"
|
| 758 |
+
},
|
| 759 |
+
"node_modules/send": {
|
| 760 |
+
"version": "0.19.2",
|
| 761 |
+
"resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
|
| 762 |
+
"integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
|
| 763 |
+
"license": "MIT",
|
| 764 |
+
"dependencies": {
|
| 765 |
+
"debug": "2.6.9",
|
| 766 |
+
"depd": "2.0.0",
|
| 767 |
+
"destroy": "1.2.0",
|
| 768 |
+
"encodeurl": "~2.0.0",
|
| 769 |
+
"escape-html": "~1.0.3",
|
| 770 |
+
"etag": "~1.8.1",
|
| 771 |
+
"fresh": "~0.5.2",
|
| 772 |
+
"http-errors": "~2.0.1",
|
| 773 |
+
"mime": "1.6.0",
|
| 774 |
+
"ms": "2.1.3",
|
| 775 |
+
"on-finished": "~2.4.1",
|
| 776 |
+
"range-parser": "~1.2.1",
|
| 777 |
+
"statuses": "~2.0.2"
|
| 778 |
+
},
|
| 779 |
+
"engines": {
|
| 780 |
+
"node": ">= 0.8.0"
|
| 781 |
+
}
|
| 782 |
+
},
|
| 783 |
+
"node_modules/send/node_modules/ms": {
|
| 784 |
+
"version": "2.1.3",
|
| 785 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 786 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 787 |
+
"license": "MIT"
|
| 788 |
+
},
|
| 789 |
+
"node_modules/serve-static": {
|
| 790 |
+
"version": "1.16.3",
|
| 791 |
+
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
|
| 792 |
+
"integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
|
| 793 |
+
"license": "MIT",
|
| 794 |
+
"dependencies": {
|
| 795 |
+
"encodeurl": "~2.0.0",
|
| 796 |
+
"escape-html": "~1.0.3",
|
| 797 |
+
"parseurl": "~1.3.3",
|
| 798 |
+
"send": "~0.19.1"
|
| 799 |
+
},
|
| 800 |
+
"engines": {
|
| 801 |
+
"node": ">= 0.8.0"
|
| 802 |
+
}
|
| 803 |
+
},
|
| 804 |
+
"node_modules/setprototypeof": {
|
| 805 |
+
"version": "1.2.0",
|
| 806 |
+
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
| 807 |
+
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
| 808 |
+
"license": "ISC"
|
| 809 |
+
},
|
| 810 |
+
"node_modules/side-channel": {
|
| 811 |
+
"version": "1.1.0",
|
| 812 |
+
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
| 813 |
+
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
| 814 |
+
"license": "MIT",
|
| 815 |
+
"dependencies": {
|
| 816 |
+
"es-errors": "^1.3.0",
|
| 817 |
+
"object-inspect": "^1.13.3",
|
| 818 |
+
"side-channel-list": "^1.0.0",
|
| 819 |
+
"side-channel-map": "^1.0.1",
|
| 820 |
+
"side-channel-weakmap": "^1.0.2"
|
| 821 |
+
},
|
| 822 |
+
"engines": {
|
| 823 |
+
"node": ">= 0.4"
|
| 824 |
+
},
|
| 825 |
+
"funding": {
|
| 826 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 827 |
+
}
|
| 828 |
+
},
|
| 829 |
+
"node_modules/side-channel-list": {
|
| 830 |
+
"version": "1.0.0",
|
| 831 |
+
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
| 832 |
+
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
| 833 |
+
"license": "MIT",
|
| 834 |
+
"dependencies": {
|
| 835 |
+
"es-errors": "^1.3.0",
|
| 836 |
+
"object-inspect": "^1.13.3"
|
| 837 |
+
},
|
| 838 |
+
"engines": {
|
| 839 |
+
"node": ">= 0.4"
|
| 840 |
+
},
|
| 841 |
+
"funding": {
|
| 842 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 843 |
+
}
|
| 844 |
+
},
|
| 845 |
+
"node_modules/side-channel-map": {
|
| 846 |
+
"version": "1.0.1",
|
| 847 |
+
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
| 848 |
+
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
| 849 |
+
"license": "MIT",
|
| 850 |
+
"dependencies": {
|
| 851 |
+
"call-bound": "^1.0.2",
|
| 852 |
+
"es-errors": "^1.3.0",
|
| 853 |
+
"get-intrinsic": "^1.2.5",
|
| 854 |
+
"object-inspect": "^1.13.3"
|
| 855 |
+
},
|
| 856 |
+
"engines": {
|
| 857 |
+
"node": ">= 0.4"
|
| 858 |
+
},
|
| 859 |
+
"funding": {
|
| 860 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 861 |
+
}
|
| 862 |
+
},
|
| 863 |
+
"node_modules/side-channel-weakmap": {
|
| 864 |
+
"version": "1.0.2",
|
| 865 |
+
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
| 866 |
+
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
| 867 |
+
"license": "MIT",
|
| 868 |
+
"dependencies": {
|
| 869 |
+
"call-bound": "^1.0.2",
|
| 870 |
+
"es-errors": "^1.3.0",
|
| 871 |
+
"get-intrinsic": "^1.2.5",
|
| 872 |
+
"object-inspect": "^1.13.3",
|
| 873 |
+
"side-channel-map": "^1.0.1"
|
| 874 |
+
},
|
| 875 |
+
"engines": {
|
| 876 |
+
"node": ">= 0.4"
|
| 877 |
+
},
|
| 878 |
+
"funding": {
|
| 879 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 880 |
+
}
|
| 881 |
+
},
|
| 882 |
+
"node_modules/socket.io": {
|
| 883 |
+
"version": "4.8.3",
|
| 884 |
+
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz",
|
| 885 |
+
"integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==",
|
| 886 |
+
"license": "MIT",
|
| 887 |
+
"dependencies": {
|
| 888 |
+
"accepts": "~1.3.4",
|
| 889 |
+
"base64id": "~2.0.0",
|
| 890 |
+
"cors": "~2.8.5",
|
| 891 |
+
"debug": "~4.4.1",
|
| 892 |
+
"engine.io": "~6.6.0",
|
| 893 |
+
"socket.io-adapter": "~2.5.2",
|
| 894 |
+
"socket.io-parser": "~4.2.4"
|
| 895 |
+
},
|
| 896 |
+
"engines": {
|
| 897 |
+
"node": ">=10.2.0"
|
| 898 |
+
}
|
| 899 |
+
},
|
| 900 |
+
"node_modules/socket.io-adapter": {
|
| 901 |
+
"version": "2.5.6",
|
| 902 |
+
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz",
|
| 903 |
+
"integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==",
|
| 904 |
+
"license": "MIT",
|
| 905 |
+
"dependencies": {
|
| 906 |
+
"debug": "~4.4.1",
|
| 907 |
+
"ws": "~8.18.3"
|
| 908 |
+
}
|
| 909 |
+
},
|
| 910 |
+
"node_modules/socket.io-adapter/node_modules/debug": {
|
| 911 |
+
"version": "4.4.3",
|
| 912 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
| 913 |
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
| 914 |
+
"license": "MIT",
|
| 915 |
+
"dependencies": {
|
| 916 |
+
"ms": "^2.1.3"
|
| 917 |
+
},
|
| 918 |
+
"engines": {
|
| 919 |
+
"node": ">=6.0"
|
| 920 |
+
},
|
| 921 |
+
"peerDependenciesMeta": {
|
| 922 |
+
"supports-color": {
|
| 923 |
+
"optional": true
|
| 924 |
+
}
|
| 925 |
+
}
|
| 926 |
+
},
|
| 927 |
+
"node_modules/socket.io-adapter/node_modules/ms": {
|
| 928 |
+
"version": "2.1.3",
|
| 929 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 930 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 931 |
+
"license": "MIT"
|
| 932 |
+
},
|
| 933 |
+
"node_modules/socket.io-parser": {
|
| 934 |
+
"version": "4.2.5",
|
| 935 |
+
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz",
|
| 936 |
+
"integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==",
|
| 937 |
+
"license": "MIT",
|
| 938 |
+
"dependencies": {
|
| 939 |
+
"@socket.io/component-emitter": "~3.1.0",
|
| 940 |
+
"debug": "~4.4.1"
|
| 941 |
+
},
|
| 942 |
+
"engines": {
|
| 943 |
+
"node": ">=10.0.0"
|
| 944 |
+
}
|
| 945 |
+
},
|
| 946 |
+
"node_modules/socket.io-parser/node_modules/debug": {
|
| 947 |
+
"version": "4.4.3",
|
| 948 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
| 949 |
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
| 950 |
+
"license": "MIT",
|
| 951 |
+
"dependencies": {
|
| 952 |
+
"ms": "^2.1.3"
|
| 953 |
+
},
|
| 954 |
+
"engines": {
|
| 955 |
+
"node": ">=6.0"
|
| 956 |
+
},
|
| 957 |
+
"peerDependenciesMeta": {
|
| 958 |
+
"supports-color": {
|
| 959 |
+
"optional": true
|
| 960 |
+
}
|
| 961 |
+
}
|
| 962 |
+
},
|
| 963 |
+
"node_modules/socket.io-parser/node_modules/ms": {
|
| 964 |
+
"version": "2.1.3",
|
| 965 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 966 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 967 |
+
"license": "MIT"
|
| 968 |
+
},
|
| 969 |
+
"node_modules/socket.io/node_modules/debug": {
|
| 970 |
+
"version": "4.4.3",
|
| 971 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
| 972 |
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
| 973 |
+
"license": "MIT",
|
| 974 |
+
"dependencies": {
|
| 975 |
+
"ms": "^2.1.3"
|
| 976 |
+
},
|
| 977 |
+
"engines": {
|
| 978 |
+
"node": ">=6.0"
|
| 979 |
+
},
|
| 980 |
+
"peerDependenciesMeta": {
|
| 981 |
+
"supports-color": {
|
| 982 |
+
"optional": true
|
| 983 |
+
}
|
| 984 |
+
}
|
| 985 |
+
},
|
| 986 |
+
"node_modules/socket.io/node_modules/ms": {
|
| 987 |
+
"version": "2.1.3",
|
| 988 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 989 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 990 |
+
"license": "MIT"
|
| 991 |
+
},
|
| 992 |
+
"node_modules/statuses": {
|
| 993 |
+
"version": "2.0.2",
|
| 994 |
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
| 995 |
+
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
| 996 |
+
"license": "MIT",
|
| 997 |
+
"engines": {
|
| 998 |
+
"node": ">= 0.8"
|
| 999 |
+
}
|
| 1000 |
+
},
|
| 1001 |
+
"node_modules/toidentifier": {
|
| 1002 |
+
"version": "1.0.1",
|
| 1003 |
+
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
| 1004 |
+
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
| 1005 |
+
"license": "MIT",
|
| 1006 |
+
"engines": {
|
| 1007 |
+
"node": ">=0.6"
|
| 1008 |
+
}
|
| 1009 |
+
},
|
| 1010 |
+
"node_modules/type-is": {
|
| 1011 |
+
"version": "1.6.18",
|
| 1012 |
+
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
| 1013 |
+
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
| 1014 |
+
"license": "MIT",
|
| 1015 |
+
"dependencies": {
|
| 1016 |
+
"media-typer": "0.3.0",
|
| 1017 |
+
"mime-types": "~2.1.24"
|
| 1018 |
+
},
|
| 1019 |
+
"engines": {
|
| 1020 |
+
"node": ">= 0.6"
|
| 1021 |
+
}
|
| 1022 |
+
},
|
| 1023 |
+
"node_modules/undici-types": {
|
| 1024 |
+
"version": "7.18.2",
|
| 1025 |
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
| 1026 |
+
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
| 1027 |
+
"license": "MIT"
|
| 1028 |
+
},
|
| 1029 |
+
"node_modules/unpipe": {
|
| 1030 |
+
"version": "1.0.0",
|
| 1031 |
+
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
| 1032 |
+
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
| 1033 |
+
"license": "MIT",
|
| 1034 |
+
"engines": {
|
| 1035 |
+
"node": ">= 0.8"
|
| 1036 |
+
}
|
| 1037 |
+
},
|
| 1038 |
+
"node_modules/utils-merge": {
|
| 1039 |
+
"version": "1.0.1",
|
| 1040 |
+
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
| 1041 |
+
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
| 1042 |
+
"license": "MIT",
|
| 1043 |
+
"engines": {
|
| 1044 |
+
"node": ">= 0.4.0"
|
| 1045 |
+
}
|
| 1046 |
+
},
|
| 1047 |
+
"node_modules/vary": {
|
| 1048 |
+
"version": "1.1.2",
|
| 1049 |
+
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
| 1050 |
+
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
| 1051 |
+
"license": "MIT",
|
| 1052 |
+
"engines": {
|
| 1053 |
+
"node": ">= 0.8"
|
| 1054 |
+
}
|
| 1055 |
+
},
|
| 1056 |
+
"node_modules/ws": {
|
| 1057 |
+
"version": "8.18.3",
|
| 1058 |
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
| 1059 |
+
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
| 1060 |
+
"license": "MIT",
|
| 1061 |
+
"engines": {
|
| 1062 |
+
"node": ">=10.0.0"
|
| 1063 |
+
},
|
| 1064 |
+
"peerDependencies": {
|
| 1065 |
+
"bufferutil": "^4.0.1",
|
| 1066 |
+
"utf-8-validate": ">=5.0.2"
|
| 1067 |
+
},
|
| 1068 |
+
"peerDependenciesMeta": {
|
| 1069 |
+
"bufferutil": {
|
| 1070 |
+
"optional": true
|
| 1071 |
+
},
|
| 1072 |
+
"utf-8-validate": {
|
| 1073 |
+
"optional": true
|
| 1074 |
+
}
|
| 1075 |
+
}
|
| 1076 |
+
}
|
| 1077 |
+
}
|
| 1078 |
+
}
|
package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "agent-bridge",
|
| 3 |
+
"version": "2.0.0",
|
| 4 |
+
"description": "Webapp-to-Antigravity-CLI bridging framework with MCP-like tool system",
|
| 5 |
+
"private": true,
|
| 6 |
+
"scripts": {
|
| 7 |
+
"dev": "npx tsx server/index.ts",
|
| 8 |
+
"dev:client": "cd client && npx vite --port 5173",
|
| 9 |
+
"start": "npx tsx server/index.ts",
|
| 10 |
+
"build": "cd client && npx vite build"
|
| 11 |
+
},
|
| 12 |
+
"dependencies": {
|
| 13 |
+
"cors": "^2.8.5",
|
| 14 |
+
"express": "^4.21.0",
|
| 15 |
+
"socket.io": "^4.7.5"
|
| 16 |
+
},
|
| 17 |
+
"devDependencies": {
|
| 18 |
+
"@types/cors": "^2.8.17",
|
| 19 |
+
"@types/express": "^4.17.21",
|
| 20 |
+
"@types/node": "^22.0.0",
|
| 21 |
+
"tsx": "^4.19.0",
|
| 22 |
+
"typescript": "^5.6.0",
|
| 23 |
+
"vite": "^5.4.0"
|
| 24 |
+
},
|
| 25 |
+
"author": "Rembrant Oyangoren Albeos",
|
| 26 |
+
"license": "MIT"
|
| 27 |
+
}
|
server/bridge.ts
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ---------------------------------------------------------------------------
|
| 2 |
+
// Bridge -- Real persistent Antigravity CLI session management
|
| 3 |
+
// ---------------------------------------------------------------------------
|
| 4 |
+
// Spawns the Antigravity CLI as a persistent interactive subprocess.
|
| 5 |
+
// Relays stdin/stdout/stderr over WebSocket so the webapp mirrors the CLI.
|
| 6 |
+
// ---------------------------------------------------------------------------
|
| 7 |
+
|
| 8 |
+
import { Server, Socket } from 'socket.io';
|
| 9 |
+
import { spawn, ChildProcess } from 'child_process';
|
| 10 |
+
import { ToolRegistry, ToolResult } from './toolRegistry';
|
| 11 |
+
import { CLIDetectionResult, detectCLI } from './cliDetector';
|
| 12 |
+
import fs from 'fs';
|
| 13 |
+
import path from 'path';
|
| 14 |
+
|
| 15 |
+
const SETTINGS_FILE = path.join(__dirname, '..', 'settings.json');
|
| 16 |
+
|
| 17 |
+
interface Settings {
|
| 18 |
+
cliPath: string;
|
| 19 |
+
autoConnect: boolean;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
interface CLISession {
|
| 23 |
+
process: ChildProcess;
|
| 24 |
+
logs: string[];
|
| 25 |
+
started: number;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
// Persistent state
|
| 29 |
+
let currentSession: CLISession | null = null;
|
| 30 |
+
let settings: Settings = loadSettings();
|
| 31 |
+
let latestCliStatus: CLIDetectionResult | null = null;
|
| 32 |
+
|
| 33 |
+
function loadSettings(): Settings {
|
| 34 |
+
try {
|
| 35 |
+
if (fs.existsSync(SETTINGS_FILE)) {
|
| 36 |
+
return JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf-8'));
|
| 37 |
+
}
|
| 38 |
+
} catch { }
|
| 39 |
+
return { cliPath: '', autoConnect: false };
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
function saveSettings(s: Settings): void {
|
| 43 |
+
settings = s;
|
| 44 |
+
fs.writeFileSync(SETTINGS_FILE, JSON.stringify(s, null, 2), 'utf-8');
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
export function getSettings(): Settings {
|
| 48 |
+
return settings;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
export function updateSettings(partial: Partial<Settings>): Settings {
|
| 52 |
+
settings = { ...settings, ...partial };
|
| 53 |
+
saveSettings(settings);
|
| 54 |
+
return settings;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
export async function redetectCLI(): Promise<CLIDetectionResult> {
|
| 58 |
+
// Override detection with user-configured path
|
| 59 |
+
if (settings.cliPath) {
|
| 60 |
+
process.env.ANTIGRAVITY_CLI_PATH = settings.cliPath;
|
| 61 |
+
}
|
| 62 |
+
latestCliStatus = await detectCLI();
|
| 63 |
+
return latestCliStatus;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
export function setupBridge(
|
| 67 |
+
io: Server,
|
| 68 |
+
toolRegistry: ToolRegistry,
|
| 69 |
+
cliStatus: CLIDetectionResult
|
| 70 |
+
): void {
|
| 71 |
+
latestCliStatus = cliStatus;
|
| 72 |
+
|
| 73 |
+
io.on('connection', (socket: Socket) => {
|
| 74 |
+
console.log(` [Bridge] Client connected: ${socket.id}`);
|
| 75 |
+
|
| 76 |
+
// Send initial state
|
| 77 |
+
socket.emit('bridge:init', {
|
| 78 |
+
tools: toolRegistry.listTools(),
|
| 79 |
+
cli: {
|
| 80 |
+
detected: latestCliStatus!.detected,
|
| 81 |
+
version: latestCliStatus!.version,
|
| 82 |
+
method: latestCliStatus!.method,
|
| 83 |
+
path: latestCliStatus!.path,
|
| 84 |
+
},
|
| 85 |
+
settings: settings,
|
| 86 |
+
sessionActive: currentSession !== null,
|
| 87 |
+
serverTime: new Date().toISOString(),
|
| 88 |
+
});
|
| 89 |
+
|
| 90 |
+
// ------------------------------------------------------------------
|
| 91 |
+
// Settings management (from UI)
|
| 92 |
+
// ------------------------------------------------------------------
|
| 93 |
+
socket.on('settings:get', (callback: Function) => {
|
| 94 |
+
if (typeof callback === 'function') {
|
| 95 |
+
callback({ settings, cli: latestCliStatus });
|
| 96 |
+
}
|
| 97 |
+
});
|
| 98 |
+
|
| 99 |
+
socket.on('settings:update', async (data: Partial<Settings>, callback: Function) => {
|
| 100 |
+
const updated = updateSettings(data);
|
| 101 |
+
console.log(` [Bridge] Settings updated:`, updated);
|
| 102 |
+
|
| 103 |
+
// Re-detect with new path
|
| 104 |
+
const newStatus = await redetectCLI();
|
| 105 |
+
latestCliStatus = newStatus;
|
| 106 |
+
|
| 107 |
+
// Notify all clients
|
| 108 |
+
io.emit('settings:changed', { settings: updated, cli: newStatus });
|
| 109 |
+
|
| 110 |
+
if (typeof callback === 'function') {
|
| 111 |
+
callback({ settings: updated, cli: newStatus });
|
| 112 |
+
}
|
| 113 |
+
});
|
| 114 |
+
|
| 115 |
+
// ------------------------------------------------------------------
|
| 116 |
+
// CLI Session management
|
| 117 |
+
// ------------------------------------------------------------------
|
| 118 |
+
socket.on('cli:start', async (data: { cliPath?: string }) => {
|
| 119 |
+
const cliPath = data?.cliPath || settings.cliPath || latestCliStatus?.path;
|
| 120 |
+
|
| 121 |
+
if (!cliPath) {
|
| 122 |
+
socket.emit('cli:error', {
|
| 123 |
+
message: 'No CLI path configured. Go to Settings and set the path to your Antigravity CLI executable.',
|
| 124 |
+
});
|
| 125 |
+
return;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
// Kill existing session
|
| 129 |
+
if (currentSession) {
|
| 130 |
+
try { currentSession.process.kill('SIGTERM'); } catch { }
|
| 131 |
+
currentSession = null;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
console.log(` [Bridge] Starting CLI session: ${cliPath}`);
|
| 135 |
+
io.emit('cli:starting', { path: cliPath });
|
| 136 |
+
|
| 137 |
+
try {
|
| 138 |
+
const child = spawn(cliPath, [], {
|
| 139 |
+
shell: true,
|
| 140 |
+
cwd: process.cwd(),
|
| 141 |
+
env: { ...process.env, TERM: 'dumb', NO_COLOR: '1' },
|
| 142 |
+
stdio: ['pipe', 'pipe', 'pipe'],
|
| 143 |
+
});
|
| 144 |
+
|
| 145 |
+
currentSession = {
|
| 146 |
+
process: child,
|
| 147 |
+
logs: [],
|
| 148 |
+
started: Date.now(),
|
| 149 |
+
};
|
| 150 |
+
|
| 151 |
+
child.stdout?.on('data', (chunk: Buffer) => {
|
| 152 |
+
const text = chunk.toString();
|
| 153 |
+
currentSession?.logs.push(text);
|
| 154 |
+
io.emit('cli:stdout', { data: text, timestamp: Date.now() });
|
| 155 |
+
});
|
| 156 |
+
|
| 157 |
+
child.stderr?.on('data', (chunk: Buffer) => {
|
| 158 |
+
const text = chunk.toString();
|
| 159 |
+
currentSession?.logs.push(`[stderr] ${text}`);
|
| 160 |
+
io.emit('cli:stderr', { data: text, timestamp: Date.now() });
|
| 161 |
+
});
|
| 162 |
+
|
| 163 |
+
child.on('close', (code: number | null) => {
|
| 164 |
+
console.log(` [Bridge] CLI session ended: exit code ${code}`);
|
| 165 |
+
io.emit('cli:ended', { exitCode: code });
|
| 166 |
+
currentSession = null;
|
| 167 |
+
});
|
| 168 |
+
|
| 169 |
+
child.on('error', (err: Error) => {
|
| 170 |
+
console.error(` [Bridge] CLI error: ${err.message}`);
|
| 171 |
+
io.emit('cli:error', { message: `Failed to start CLI: ${err.message}` });
|
| 172 |
+
currentSession = null;
|
| 173 |
+
});
|
| 174 |
+
|
| 175 |
+
io.emit('cli:started', { path: cliPath, timestamp: Date.now() });
|
| 176 |
+
} catch (err: any) {
|
| 177 |
+
io.emit('cli:error', { message: `Failed to spawn CLI: ${err.message}` });
|
| 178 |
+
}
|
| 179 |
+
});
|
| 180 |
+
|
| 181 |
+
socket.on('cli:stop', () => {
|
| 182 |
+
if (currentSession) {
|
| 183 |
+
try { currentSession.process.kill('SIGTERM'); } catch { }
|
| 184 |
+
currentSession = null;
|
| 185 |
+
io.emit('cli:ended', { exitCode: null, reason: 'user-stopped' });
|
| 186 |
+
}
|
| 187 |
+
});
|
| 188 |
+
|
| 189 |
+
socket.on('cli:status', (callback: Function) => {
|
| 190 |
+
if (typeof callback === 'function') {
|
| 191 |
+
callback({
|
| 192 |
+
active: currentSession !== null,
|
| 193 |
+
uptime: currentSession ? Date.now() - currentSession.started : 0,
|
| 194 |
+
logLength: currentSession?.logs.length || 0,
|
| 195 |
+
});
|
| 196 |
+
}
|
| 197 |
+
});
|
| 198 |
+
|
| 199 |
+
// ------------------------------------------------------------------
|
| 200 |
+
// Prompt handling -- relay to CLI or match tools
|
| 201 |
+
// ------------------------------------------------------------------
|
| 202 |
+
socket.on('prompt:send', async (data: { message: string; id: string }) => {
|
| 203 |
+
const { message, id } = data;
|
| 204 |
+
console.log(` [Bridge] Prompt: ${message}`);
|
| 205 |
+
|
| 206 |
+
// Broadcast to all clients
|
| 207 |
+
io.emit('prompt:received', {
|
| 208 |
+
message, id, from: 'human',
|
| 209 |
+
timestamp: new Date().toISOString(),
|
| 210 |
+
});
|
| 211 |
+
|
| 212 |
+
// Check for tool match first
|
| 213 |
+
const match = toolRegistry.matchTool(message);
|
| 214 |
+
|
| 215 |
+
if (match) {
|
| 216 |
+
const { tool, params } = match;
|
| 217 |
+
console.log(` [Bridge] Tool matched: ${tool.name}`);
|
| 218 |
+
|
| 219 |
+
io.emit('tool:started', { id, toolName: tool.name, params, timestamp: new Date().toISOString() });
|
| 220 |
+
|
| 221 |
+
try {
|
| 222 |
+
const result: ToolResult = await tool.execute(params, (progress: string) => {
|
| 223 |
+
io.emit('tool:progress', { id, toolName: tool.name, progress, timestamp: new Date().toISOString() });
|
| 224 |
+
});
|
| 225 |
+
|
| 226 |
+
io.emit('tool:completed', { id, toolName: tool.name, result, timestamp: new Date().toISOString() });
|
| 227 |
+
} catch (err: any) {
|
| 228 |
+
io.emit('tool:error', { id, toolName: tool.name, error: err.message, timestamp: new Date().toISOString() });
|
| 229 |
+
}
|
| 230 |
+
} else if (currentSession?.process?.stdin) {
|
| 231 |
+
// Send prompt to the running CLI process
|
| 232 |
+
try {
|
| 233 |
+
currentSession.process.stdin.write(message + '\n');
|
| 234 |
+
console.log(` [Bridge] Sent to CLI stdin: ${message}`);
|
| 235 |
+
} catch (err: any) {
|
| 236 |
+
io.emit('cli:error', { message: `Failed to write to CLI: ${err.message}` });
|
| 237 |
+
}
|
| 238 |
+
} else {
|
| 239 |
+
// No tool match, no CLI session
|
| 240 |
+
io.emit('agent:message', {
|
| 241 |
+
id, from: 'system',
|
| 242 |
+
message: currentSession
|
| 243 |
+
? 'CLI session is active but stdin is unavailable.'
|
| 244 |
+
: 'No CLI session is running. Click "Connect" in Settings to start the Antigravity CLI, or configure the path first.',
|
| 245 |
+
timestamp: new Date().toISOString(),
|
| 246 |
+
});
|
| 247 |
+
}
|
| 248 |
+
});
|
| 249 |
+
|
| 250 |
+
// ------------------------------------------------------------------
|
| 251 |
+
// MCP tool discovery & invocation
|
| 252 |
+
// ------------------------------------------------------------------
|
| 253 |
+
socket.on('tool:list', (callback: Function) => {
|
| 254 |
+
if (typeof callback === 'function') {
|
| 255 |
+
callback({ status: 'success', tools: toolRegistry.listTools() });
|
| 256 |
+
}
|
| 257 |
+
});
|
| 258 |
+
|
| 259 |
+
socket.on('tool:invoke', async (data: { toolName: string; params: any }, callback: Function) => {
|
| 260 |
+
const tool = toolRegistry.getTool(data.toolName);
|
| 261 |
+
if (!tool) {
|
| 262 |
+
if (typeof callback === 'function') callback({ status: 'error', message: `Tool "${data.toolName}" not found.` });
|
| 263 |
+
return;
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
try {
|
| 267 |
+
const result = await tool.execute(data.params, (progress: string) => {
|
| 268 |
+
io.emit('tool:progress', { toolName: data.toolName, progress, timestamp: new Date().toISOString() });
|
| 269 |
+
});
|
| 270 |
+
if (typeof callback === 'function') callback({ status: 'success', result });
|
| 271 |
+
} catch (err: any) {
|
| 272 |
+
if (typeof callback === 'function') callback({ status: 'error', message: err.message });
|
| 273 |
+
}
|
| 274 |
+
});
|
| 275 |
+
|
| 276 |
+
socket.on('agent:response', (data: any) => {
|
| 277 |
+
io.emit('agent:message', { ...data, from: 'agent', timestamp: new Date().toISOString() });
|
| 278 |
+
});
|
| 279 |
+
|
| 280 |
+
socket.on('disconnect', () => {
|
| 281 |
+
console.log(` [Bridge] Client disconnected: ${socket.id}`);
|
| 282 |
+
// Don't kill CLI session on disconnect -- it persists
|
| 283 |
+
});
|
| 284 |
+
});
|
| 285 |
+
}
|
server/cliDetector.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ---------------------------------------------------------------------------
|
| 2 |
+
// CLI Detector -- Real auto-detection of Google Antigravity CLI
|
| 3 |
+
// ---------------------------------------------------------------------------
|
| 4 |
+
// Multi-strategy detection:
|
| 5 |
+
// 1. ANTIGRAVITY_CLI_PATH env var
|
| 6 |
+
// 2. Search system PATH
|
| 7 |
+
// 3. Check common install locations
|
| 8 |
+
// 4. Check npm global installs
|
| 9 |
+
// 5. Probe for Gemini CLI as alternative
|
| 10 |
+
// ---------------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
import { execSync } from 'child_process';
|
| 13 |
+
import fs from 'fs';
|
| 14 |
+
import path from 'path';
|
| 15 |
+
import os from 'os';
|
| 16 |
+
|
| 17 |
+
export interface CLIDetectionResult {
|
| 18 |
+
detected: boolean;
|
| 19 |
+
path: string | null;
|
| 20 |
+
version: string | null;
|
| 21 |
+
method: string;
|
| 22 |
+
candidates: string[];
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
const CLI_NAMES = [
|
| 26 |
+
'antigravity',
|
| 27 |
+
'antigravity.exe',
|
| 28 |
+
'antigravity.cmd',
|
| 29 |
+
'gemini',
|
| 30 |
+
'gemini.exe',
|
| 31 |
+
'gemini.cmd',
|
| 32 |
+
];
|
| 33 |
+
|
| 34 |
+
export async function detectCLI(): Promise<CLIDetectionResult> {
|
| 35 |
+
const candidates: string[] = [];
|
| 36 |
+
const result: CLIDetectionResult = {
|
| 37 |
+
detected: false,
|
| 38 |
+
path: null,
|
| 39 |
+
version: null,
|
| 40 |
+
method: 'none',
|
| 41 |
+
candidates,
|
| 42 |
+
};
|
| 43 |
+
|
| 44 |
+
// Strategy 1: Environment variable
|
| 45 |
+
const envPath = process.env.ANTIGRAVITY_CLI_PATH;
|
| 46 |
+
if (envPath) {
|
| 47 |
+
candidates.push(`[env] ${envPath}`);
|
| 48 |
+
const version = tryGetVersion(envPath);
|
| 49 |
+
if (version) {
|
| 50 |
+
result.detected = true;
|
| 51 |
+
result.path = envPath;
|
| 52 |
+
result.version = version;
|
| 53 |
+
result.method = 'env:ANTIGRAVITY_CLI_PATH';
|
| 54 |
+
return result;
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
// Strategy 2: Search system PATH using `where` (Windows) or `which` (Unix)
|
| 59 |
+
for (const name of CLI_NAMES) {
|
| 60 |
+
const found = findOnPath(name);
|
| 61 |
+
if (found) {
|
| 62 |
+
candidates.push(`[PATH] ${found}`);
|
| 63 |
+
const version = tryGetVersion(found);
|
| 64 |
+
if (version) {
|
| 65 |
+
result.detected = true;
|
| 66 |
+
result.path = found;
|
| 67 |
+
result.version = version;
|
| 68 |
+
result.method = `PATH:${name}`;
|
| 69 |
+
return result;
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
// Strategy 3: Common install locations
|
| 75 |
+
const commonPaths = getCommonInstallPaths();
|
| 76 |
+
for (const candidate of commonPaths) {
|
| 77 |
+
candidates.push(`[common] ${candidate}`);
|
| 78 |
+
if (fs.existsSync(candidate)) {
|
| 79 |
+
const version = tryGetVersion(candidate);
|
| 80 |
+
if (version) {
|
| 81 |
+
result.detected = true;
|
| 82 |
+
result.path = candidate;
|
| 83 |
+
result.version = version;
|
| 84 |
+
result.method = 'common-path';
|
| 85 |
+
return result;
|
| 86 |
+
}
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
// Strategy 4: npm global bin
|
| 91 |
+
try {
|
| 92 |
+
const npmBin = execSync('npm root -g', { timeout: 5000 }).toString().trim();
|
| 93 |
+
const npmParent = path.dirname(npmBin);
|
| 94 |
+
for (const name of CLI_NAMES) {
|
| 95 |
+
const candidate = path.join(npmParent, name);
|
| 96 |
+
candidates.push(`[npm-global] ${candidate}`);
|
| 97 |
+
if (fs.existsSync(candidate)) {
|
| 98 |
+
const version = tryGetVersion(candidate);
|
| 99 |
+
if (version) {
|
| 100 |
+
result.detected = true;
|
| 101 |
+
result.path = candidate;
|
| 102 |
+
result.version = version;
|
| 103 |
+
result.method = 'npm-global';
|
| 104 |
+
return result;
|
| 105 |
+
}
|
| 106 |
+
}
|
| 107 |
+
}
|
| 108 |
+
} catch {
|
| 109 |
+
// npm not available
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
// Strategy 5: Try bare command execution as last resort
|
| 113 |
+
for (const name of ['antigravity', 'gemini']) {
|
| 114 |
+
candidates.push(`[bare] ${name}`);
|
| 115 |
+
const version = tryGetVersion(name);
|
| 116 |
+
if (version) {
|
| 117 |
+
result.detected = true;
|
| 118 |
+
result.path = name;
|
| 119 |
+
result.version = version;
|
| 120 |
+
result.method = `bare:${name}`;
|
| 121 |
+
return result;
|
| 122 |
+
}
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
return result;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
function findOnPath(name: string): string | null {
|
| 129 |
+
try {
|
| 130 |
+
const cmd = process.platform === 'win32' ? `where ${name}` : `which ${name}`;
|
| 131 |
+
const result = execSync(cmd, { timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] })
|
| 132 |
+
.toString()
|
| 133 |
+
.trim()
|
| 134 |
+
.split('\n')[0]
|
| 135 |
+
.trim();
|
| 136 |
+
return result || null;
|
| 137 |
+
} catch {
|
| 138 |
+
return null;
|
| 139 |
+
}
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
function tryGetVersion(cliPath: string): string | null {
|
| 143 |
+
const commands = [
|
| 144 |
+
`"${cliPath}" --version`,
|
| 145 |
+
`"${cliPath}" -v`,
|
| 146 |
+
`"${cliPath}" version`,
|
| 147 |
+
];
|
| 148 |
+
|
| 149 |
+
for (const cmd of commands) {
|
| 150 |
+
try {
|
| 151 |
+
const output = execSync(cmd, {
|
| 152 |
+
timeout: 8000,
|
| 153 |
+
stdio: ['pipe', 'pipe', 'pipe'],
|
| 154 |
+
env: { ...process.env },
|
| 155 |
+
}).toString().trim();
|
| 156 |
+
|
| 157 |
+
if (output && output.length < 200) {
|
| 158 |
+
return output;
|
| 159 |
+
}
|
| 160 |
+
} catch {
|
| 161 |
+
// continue to next command variant
|
| 162 |
+
}
|
| 163 |
+
}
|
| 164 |
+
return null;
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
function getCommonInstallPaths(): string[] {
|
| 168 |
+
const home = os.homedir();
|
| 169 |
+
const paths: string[] = [];
|
| 170 |
+
|
| 171 |
+
if (process.platform === 'win32') {
|
| 172 |
+
paths.push(
|
| 173 |
+
path.join(home, 'AppData', 'Local', 'Programs', 'antigravity', 'antigravity.exe'),
|
| 174 |
+
path.join(home, 'AppData', 'Local', 'antigravity', 'antigravity.exe'),
|
| 175 |
+
path.join(home, 'AppData', 'Roaming', 'npm', 'antigravity.cmd'),
|
| 176 |
+
'C:\\Program Files\\Google\\Antigravity\\antigravity.exe',
|
| 177 |
+
'C:\\Program Files (x86)\\Google\\Antigravity\\antigravity.exe',
|
| 178 |
+
path.join(home, '.local', 'bin', 'antigravity'),
|
| 179 |
+
// Gemini CLI alternatives
|
| 180 |
+
path.join(home, 'AppData', 'Local', 'Programs', 'gemini', 'gemini.exe'),
|
| 181 |
+
path.join(home, 'AppData', 'Roaming', 'npm', 'gemini.cmd'),
|
| 182 |
+
);
|
| 183 |
+
} else {
|
| 184 |
+
paths.push(
|
| 185 |
+
'/usr/local/bin/antigravity',
|
| 186 |
+
'/usr/bin/antigravity',
|
| 187 |
+
path.join(home, '.local', 'bin', 'antigravity'),
|
| 188 |
+
path.join(home, '.npm-global', 'bin', 'antigravity'),
|
| 189 |
+
'/usr/local/bin/gemini',
|
| 190 |
+
path.join(home, '.local', 'bin', 'gemini'),
|
| 191 |
+
);
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
return paths;
|
| 195 |
+
}
|
server/index.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ---------------------------------------------------------------------------
|
| 2 |
+
// Server Entry Point
|
| 3 |
+
// ---------------------------------------------------------------------------
|
| 4 |
+
|
| 5 |
+
import express from 'express';
|
| 6 |
+
import http from 'http';
|
| 7 |
+
import { Server as SocketServer } from 'socket.io';
|
| 8 |
+
import path from 'path';
|
| 9 |
+
import cors from 'cors';
|
| 10 |
+
|
| 11 |
+
import { setupToolRegistry } from './toolRegistry';
|
| 12 |
+
import { setupBridge, getSettings, updateSettings, redetectCLI } from './bridge';
|
| 13 |
+
import { detectCLI } from './cliDetector';
|
| 14 |
+
|
| 15 |
+
const PORT = parseInt(process.env.PORT || '3777', 10);
|
| 16 |
+
|
| 17 |
+
async function main() {
|
| 18 |
+
console.log('');
|
| 19 |
+
console.log(' ==========================================');
|
| 20 |
+
console.log(' Agent Bridge v2.1');
|
| 21 |
+
console.log(' ==========================================');
|
| 22 |
+
console.log('');
|
| 23 |
+
|
| 24 |
+
// --- CLI Detection ---
|
| 25 |
+
console.log(' Detecting Antigravity CLI...');
|
| 26 |
+
|
| 27 |
+
// Respect saved settings
|
| 28 |
+
const savedSettings = getSettings();
|
| 29 |
+
if (savedSettings.cliPath) {
|
| 30 |
+
console.log(` Using saved CLI path: ${savedSettings.cliPath}`);
|
| 31 |
+
process.env.ANTIGRAVITY_CLI_PATH = savedSettings.cliPath;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
const cliStatus = await detectCLI();
|
| 35 |
+
|
| 36 |
+
if (cliStatus.detected) {
|
| 37 |
+
console.log(` CLI detected: ${cliStatus.path}`);
|
| 38 |
+
console.log(` Version: ${cliStatus.version}`);
|
| 39 |
+
console.log(` Method: ${cliStatus.method}`);
|
| 40 |
+
} else {
|
| 41 |
+
console.log(' CLI not found.');
|
| 42 |
+
console.log(' Searched:', cliStatus.candidates.length, 'locations');
|
| 43 |
+
console.log('');
|
| 44 |
+
console.log(' Configure the CLI path via the webapp Settings panel');
|
| 45 |
+
console.log(' or set ANTIGRAVITY_CLI_PATH environment variable.');
|
| 46 |
+
}
|
| 47 |
+
console.log('');
|
| 48 |
+
|
| 49 |
+
// --- Tool Registry ---
|
| 50 |
+
const toolRegistry = setupToolRegistry();
|
| 51 |
+
console.log(` Tools loaded: ${toolRegistry.listTools().length}`);
|
| 52 |
+
console.log('');
|
| 53 |
+
|
| 54 |
+
// --- Express ---
|
| 55 |
+
const app = express();
|
| 56 |
+
const server = http.createServer(app);
|
| 57 |
+
|
| 58 |
+
app.use(cors());
|
| 59 |
+
app.use(express.json());
|
| 60 |
+
|
| 61 |
+
// Serve client files
|
| 62 |
+
const clientDir = path.join(__dirname, '..', 'client');
|
| 63 |
+
app.use(express.static(clientDir));
|
| 64 |
+
|
| 65 |
+
// Output files
|
| 66 |
+
app.use('/output', express.static(path.join(__dirname, '..', 'output')));
|
| 67 |
+
|
| 68 |
+
// Download endpoint
|
| 69 |
+
app.get('/api/download/:filename', (req, res) => {
|
| 70 |
+
const filePath = path.join(__dirname, '..', 'output', req.params.filename);
|
| 71 |
+
res.download(filePath, (err) => {
|
| 72 |
+
if (err && !res.headersSent) {
|
| 73 |
+
res.status(404).json({ error: 'File not found.' });
|
| 74 |
+
}
|
| 75 |
+
});
|
| 76 |
+
});
|
| 77 |
+
|
| 78 |
+
// Health / Status
|
| 79 |
+
app.get('/api/health', async (_req, res) => {
|
| 80 |
+
res.json({
|
| 81 |
+
status: 'ok',
|
| 82 |
+
cli: { detected: cliStatus.detected, version: cliStatus.version, method: cliStatus.method },
|
| 83 |
+
tools: toolRegistry.listTools().length,
|
| 84 |
+
timestamp: new Date().toISOString(),
|
| 85 |
+
});
|
| 86 |
+
});
|
| 87 |
+
|
| 88 |
+
// Settings API (REST)
|
| 89 |
+
app.get('/api/settings', (_req, res) => {
|
| 90 |
+
res.json(getSettings());
|
| 91 |
+
});
|
| 92 |
+
|
| 93 |
+
app.post('/api/settings', async (req, res) => {
|
| 94 |
+
const updated = updateSettings(req.body);
|
| 95 |
+
const newCli = await redetectCLI();
|
| 96 |
+
res.json({ settings: updated, cli: newCli });
|
| 97 |
+
});
|
| 98 |
+
|
| 99 |
+
// Re-detect CLI
|
| 100 |
+
app.post('/api/redetect', async (_req, res) => {
|
| 101 |
+
const result = await redetectCLI();
|
| 102 |
+
res.json(result);
|
| 103 |
+
});
|
| 104 |
+
|
| 105 |
+
// --- WebSocket ---
|
| 106 |
+
const io = new SocketServer(server, {
|
| 107 |
+
cors: { origin: '*', methods: ['GET', 'POST'] },
|
| 108 |
+
maxHttpBufferSize: 10e6,
|
| 109 |
+
});
|
| 110 |
+
|
| 111 |
+
setupBridge(io, toolRegistry, cliStatus);
|
| 112 |
+
|
| 113 |
+
// --- Start ---
|
| 114 |
+
server.listen(PORT, () => {
|
| 115 |
+
console.log(' ==========================================');
|
| 116 |
+
console.log(` Running at http://localhost:${PORT}`);
|
| 117 |
+
console.log(' ==========================================');
|
| 118 |
+
console.log('');
|
| 119 |
+
});
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
main().catch((err) => {
|
| 123 |
+
console.error('Fatal error:', err);
|
| 124 |
+
process.exit(1);
|
| 125 |
+
});
|
server/toolRegistry.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ---------------------------------------------------------------------------
|
| 2 |
+
// Tool Registry -- MCP-like typed tool system
|
| 3 |
+
// ---------------------------------------------------------------------------
|
| 4 |
+
|
| 5 |
+
import fs from 'fs';
|
| 6 |
+
import path from 'path';
|
| 7 |
+
|
| 8 |
+
export interface ToolParams {
|
| 9 |
+
url?: string;
|
| 10 |
+
prompt?: string;
|
| 11 |
+
input?: string;
|
| 12 |
+
raw?: string;
|
| 13 |
+
captures?: string[];
|
| 14 |
+
[key: string]: unknown;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
export interface ToolResult {
|
| 18 |
+
transcript?: string;
|
| 19 |
+
downloadUrl?: string;
|
| 20 |
+
filename?: string;
|
| 21 |
+
method?: string;
|
| 22 |
+
message?: string;
|
| 23 |
+
mock?: boolean;
|
| 24 |
+
error?: string;
|
| 25 |
+
[key: string]: unknown;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
export type ProgressEmitter = (message: string) => void;
|
| 29 |
+
|
| 30 |
+
export interface ToolDefinition {
|
| 31 |
+
name: string;
|
| 32 |
+
description: string;
|
| 33 |
+
syntax: string;
|
| 34 |
+
pattern: RegExp;
|
| 35 |
+
mock: boolean;
|
| 36 |
+
execute: (params: ToolParams, emitProgress: ProgressEmitter) => Promise<ToolResult>;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
export interface ToolSummary {
|
| 40 |
+
name: string;
|
| 41 |
+
description: string;
|
| 42 |
+
syntax: string;
|
| 43 |
+
mock: boolean;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
export interface ToolMatch {
|
| 47 |
+
tool: ToolDefinition;
|
| 48 |
+
params: ToolParams;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
export class ToolRegistry {
|
| 52 |
+
private tools = new Map<string, ToolDefinition>();
|
| 53 |
+
|
| 54 |
+
register(tool: ToolDefinition): void {
|
| 55 |
+
if (!tool.name || !tool.execute) {
|
| 56 |
+
throw new Error('Tool must have a name and execute function.');
|
| 57 |
+
}
|
| 58 |
+
this.tools.set(tool.name, tool);
|
| 59 |
+
console.log(` [ToolRegistry] Registered: ${tool.name}`);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
listTools(): ToolSummary[] {
|
| 63 |
+
return Array.from(this.tools.values()).map((t) => ({
|
| 64 |
+
name: t.name,
|
| 65 |
+
description: t.description,
|
| 66 |
+
syntax: t.syntax,
|
| 67 |
+
mock: t.mock,
|
| 68 |
+
}));
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
getTool(name: string): ToolDefinition | null {
|
| 72 |
+
return this.tools.get(name) ?? null;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
matchTool(message: string): ToolMatch | null {
|
| 76 |
+
for (const tool of this.tools.values()) {
|
| 77 |
+
if (!tool.pattern) continue;
|
| 78 |
+
const match = message.match(tool.pattern);
|
| 79 |
+
if (match) {
|
| 80 |
+
return {
|
| 81 |
+
tool,
|
| 82 |
+
params: { ...match.groups, raw: message, captures: match.slice(1) },
|
| 83 |
+
};
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
return null;
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
export function setupToolRegistry(): ToolRegistry {
|
| 91 |
+
const registry = new ToolRegistry();
|
| 92 |
+
const toolsDir = path.join(__dirname, 'tools');
|
| 93 |
+
|
| 94 |
+
if (fs.existsSync(toolsDir)) {
|
| 95 |
+
const files = fs.readdirSync(toolsDir).filter((f) => f.endsWith('.ts') || f.endsWith('.js'));
|
| 96 |
+
for (const file of files) {
|
| 97 |
+
try {
|
| 98 |
+
const mod = require(path.join(toolsDir, file));
|
| 99 |
+
if (typeof mod.register === 'function') {
|
| 100 |
+
mod.register(registry);
|
| 101 |
+
}
|
| 102 |
+
} catch (err: any) {
|
| 103 |
+
console.error(` [ToolRegistry] Failed to load ${file}: ${err.message}`);
|
| 104 |
+
}
|
| 105 |
+
}
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
return registry;
|
| 109 |
+
}
|
server/tools/transcript.ts
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ---------------------------------------------------------------------------
|
| 2 |
+
// Transcript Tool -- YouTube URL to transcript (FIXED)
|
| 3 |
+
// ---------------------------------------------------------------------------
|
| 4 |
+
// Fix: The YouTube API fallback was silently producing empty content.
|
| 5 |
+
// This version uses proper HTTPS with cookies/consent bypass and robust
|
| 6 |
+
// caption extraction with multiple fallback strategies.
|
| 7 |
+
// ---------------------------------------------------------------------------
|
| 8 |
+
|
| 9 |
+
import { spawn, execSync } from 'child_process';
|
| 10 |
+
import fs from 'fs';
|
| 11 |
+
import path from 'path';
|
| 12 |
+
import https from 'https';
|
| 13 |
+
import http from 'http';
|
| 14 |
+
import os from 'os';
|
| 15 |
+
import type { ToolRegistry, ToolParams, ToolResult, ProgressEmitter } from '../toolRegistry';
|
| 16 |
+
|
| 17 |
+
const OUTPUT_DIR = path.join(__dirname, '..', '..', 'output');
|
| 18 |
+
|
| 19 |
+
export function register(registry: ToolRegistry): void {
|
| 20 |
+
registry.register({
|
| 21 |
+
name: 'transcript_tool',
|
| 22 |
+
description: 'Extract transcript/subtitles from a YouTube video URL.',
|
| 23 |
+
syntax: 'use <transcript_tool> <youtube-url>',
|
| 24 |
+
pattern: /use\s+<transcript_tool>\s+(?<url>https?:\/\/(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)[\w-]+[^\s]*)/i,
|
| 25 |
+
mock: false,
|
| 26 |
+
|
| 27 |
+
async execute(params: ToolParams, emitProgress: ProgressEmitter): Promise<ToolResult> {
|
| 28 |
+
const url = params.url || (params.captures && params.captures[0]);
|
| 29 |
+
if (!url) throw new Error('No URL provided.');
|
| 30 |
+
|
| 31 |
+
emitProgress('Extracting video ID...');
|
| 32 |
+
const videoId = extractVideoId(url as string);
|
| 33 |
+
if (!videoId) throw new Error('Invalid YouTube URL.');
|
| 34 |
+
|
| 35 |
+
if (!fs.existsSync(OUTPUT_DIR)) {
|
| 36 |
+
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
emitProgress(`Video ID: ${videoId}`);
|
| 40 |
+
|
| 41 |
+
// Strategy 1: Try yt-dlp (search PATH + Python Scripts)
|
| 42 |
+
const ytdlpPath = await findYtdlp(emitProgress);
|
| 43 |
+
if (ytdlpPath) {
|
| 44 |
+
emitProgress(`yt-dlp found at: ${ytdlpPath}`);
|
| 45 |
+
try {
|
| 46 |
+
const result = await fetchWithYtdlp(ytdlpPath, url as string, videoId, emitProgress);
|
| 47 |
+
if (result.transcript && (result.transcript as string).trim().length > 0) {
|
| 48 |
+
return result;
|
| 49 |
+
}
|
| 50 |
+
emitProgress('yt-dlp returned empty subtitles. Trying fallback...');
|
| 51 |
+
} catch (err: any) {
|
| 52 |
+
emitProgress(`yt-dlp failed: ${err.message}. Trying fallback...`);
|
| 53 |
+
}
|
| 54 |
+
} else {
|
| 55 |
+
emitProgress('yt-dlp not found. Using YouTube API fallback...');
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
// Strategy 2: YouTube innertube API
|
| 59 |
+
try {
|
| 60 |
+
const result = await fetchWithInnertube(videoId, emitProgress);
|
| 61 |
+
if (result.transcript && (result.transcript as string).trim().length > 0) {
|
| 62 |
+
return result;
|
| 63 |
+
}
|
| 64 |
+
emitProgress('Innertube returned empty. Trying page scrape...');
|
| 65 |
+
} catch (err: any) {
|
| 66 |
+
emitProgress(`Innertube failed: ${err.message}. Trying page scrape...`);
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
// Strategy 3: Direct page scrape for captions
|
| 70 |
+
try {
|
| 71 |
+
const result = await fetchFromPage(videoId, emitProgress);
|
| 72 |
+
if (result.transcript && (result.transcript as string).trim().length > 0) {
|
| 73 |
+
return result;
|
| 74 |
+
}
|
| 75 |
+
} catch (err: any) {
|
| 76 |
+
emitProgress(`Page scrape failed: ${err.message}`);
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
throw new Error('Could not extract transcript. The video may not have captions, or YouTube blocked the request. Install yt-dlp for best results: pip install yt-dlp');
|
| 80 |
+
},
|
| 81 |
+
});
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
function extractVideoId(url: string): string | null {
|
| 85 |
+
const patterns = [/[?&]v=([\w-]{11})/, /youtu\.be\/([\w-]{11})/, /embed\/([\w-]{11})/];
|
| 86 |
+
for (const p of patterns) {
|
| 87 |
+
const m = url.match(p);
|
| 88 |
+
if (m) return m[1];
|
| 89 |
+
}
|
| 90 |
+
return null;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
// --- Find yt-dlp: search PATH, Python Scripts dirs, pip locations ---
|
| 94 |
+
async function findYtdlp(emitProgress: ProgressEmitter): Promise<string | null> {
|
| 95 |
+
const isWin = process.platform === 'win32';
|
| 96 |
+
const exe = isWin ? 'yt-dlp.exe' : 'yt-dlp';
|
| 97 |
+
|
| 98 |
+
// 1. Check if on PATH
|
| 99 |
+
const onPath = await checkCommand('yt-dlp');
|
| 100 |
+
if (onPath) return 'yt-dlp';
|
| 101 |
+
|
| 102 |
+
emitProgress('yt-dlp not on PATH. Searching Python Scripts directories...');
|
| 103 |
+
|
| 104 |
+
// 2. Search common Python Scripts locations (Windows)
|
| 105 |
+
const home = os.homedir();
|
| 106 |
+
const candidateDirs: string[] = [];
|
| 107 |
+
|
| 108 |
+
if (isWin) {
|
| 109 |
+
// Standard pip install locations
|
| 110 |
+
candidateDirs.push(
|
| 111 |
+
path.join(home, 'AppData', 'Local', 'Programs', 'Python', 'Python313', 'Scripts'),
|
| 112 |
+
path.join(home, 'AppData', 'Local', 'Programs', 'Python', 'Python312', 'Scripts'),
|
| 113 |
+
path.join(home, 'AppData', 'Local', 'Programs', 'Python', 'Python311', 'Scripts'),
|
| 114 |
+
path.join(home, 'AppData', 'Local', 'Programs', 'Python', 'Python310', 'Scripts'),
|
| 115 |
+
path.join(home, 'AppData', 'Roaming', 'Python', 'Python313', 'Scripts'),
|
| 116 |
+
path.join(home, 'AppData', 'Roaming', 'Python', 'Python312', 'Scripts'),
|
| 117 |
+
path.join(home, 'AppData', 'Roaming', 'Python', 'Python311', 'Scripts'),
|
| 118 |
+
);
|
| 119 |
+
|
| 120 |
+
// Microsoft Store Python (the location shown in user's pip warning)
|
| 121 |
+
try {
|
| 122 |
+
const packagesDir = path.join(home, 'AppData', 'Local', 'Packages');
|
| 123 |
+
if (fs.existsSync(packagesDir)) {
|
| 124 |
+
const entries = fs.readdirSync(packagesDir);
|
| 125 |
+
for (const entry of entries) {
|
| 126 |
+
if (entry.startsWith('PythonSoftwareFoundation.Python')) {
|
| 127 |
+
// Search recursively for Scripts dir
|
| 128 |
+
const localCache = path.join(packagesDir, entry, 'LocalCache', 'local-packages');
|
| 129 |
+
if (fs.existsSync(localCache)) {
|
| 130 |
+
const pyDirs = fs.readdirSync(localCache).filter(d => d.startsWith('Python'));
|
| 131 |
+
for (const pyDir of pyDirs) {
|
| 132 |
+
candidateDirs.push(path.join(localCache, pyDir, 'Scripts'));
|
| 133 |
+
}
|
| 134 |
+
}
|
| 135 |
+
}
|
| 136 |
+
}
|
| 137 |
+
}
|
| 138 |
+
} catch { }
|
| 139 |
+
|
| 140 |
+
// Also try pip show to find the scripts directory
|
| 141 |
+
try {
|
| 142 |
+
const pipOutput = execSync('pip show yt-dlp 2>nul', { encoding: 'utf-8', timeout: 5000 });
|
| 143 |
+
const locMatch = pipOutput.match(/Location:\s*(.+)/i);
|
| 144 |
+
if (locMatch) {
|
| 145 |
+
const sitePackages = locMatch[1].trim();
|
| 146 |
+
// Scripts is typically a sibling of the site-packages dir
|
| 147 |
+
const scriptsDir = path.join(path.dirname(sitePackages), 'Scripts');
|
| 148 |
+
candidateDirs.unshift(scriptsDir); // prioritize
|
| 149 |
+
}
|
| 150 |
+
} catch { }
|
| 151 |
+
|
| 152 |
+
// Try python -m pip show
|
| 153 |
+
try {
|
| 154 |
+
const pipOutput = execSync('python -m pip show yt-dlp 2>nul', { encoding: 'utf-8', timeout: 5000 });
|
| 155 |
+
const locMatch = pipOutput.match(/Location:\s*(.+)/i);
|
| 156 |
+
if (locMatch) {
|
| 157 |
+
const sitePackages = locMatch[1].trim();
|
| 158 |
+
const scriptsDir = path.join(path.dirname(sitePackages), 'Scripts');
|
| 159 |
+
candidateDirs.unshift(scriptsDir);
|
| 160 |
+
}
|
| 161 |
+
} catch { }
|
| 162 |
+
} else {
|
| 163 |
+
// Linux/macOS
|
| 164 |
+
candidateDirs.push(
|
| 165 |
+
path.join(home, '.local', 'bin'),
|
| 166 |
+
'/usr/local/bin',
|
| 167 |
+
'/usr/bin',
|
| 168 |
+
);
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
// Check each candidate
|
| 172 |
+
for (const dir of candidateDirs) {
|
| 173 |
+
const fullPath = path.join(dir, exe);
|
| 174 |
+
if (fs.existsSync(fullPath)) {
|
| 175 |
+
emitProgress(`Found yt-dlp at: ${fullPath}`);
|
| 176 |
+
// Verify it works
|
| 177 |
+
const works = await checkCommand(`"${fullPath}"`);
|
| 178 |
+
if (works) return `"${fullPath}"`;
|
| 179 |
+
}
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
// 3. Last resort: try python -m yt_dlp
|
| 183 |
+
const pyModule = await checkCommand('python -m yt_dlp');
|
| 184 |
+
if (pyModule) {
|
| 185 |
+
emitProgress('Found yt-dlp as Python module.');
|
| 186 |
+
return 'python -m yt_dlp';
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
return null;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
function checkCommand(cmd: string): Promise<boolean> {
|
| 193 |
+
return new Promise((resolve) => {
|
| 194 |
+
const proc = spawn(cmd, ['--version'], { shell: true });
|
| 195 |
+
let resolved = false;
|
| 196 |
+
const timeout = setTimeout(() => { if (!resolved) { resolved = true; resolve(false); try { proc.kill(); } catch { } } }, 5000);
|
| 197 |
+
proc.on('close', (code) => { if (!resolved) { resolved = true; clearTimeout(timeout); resolve(code === 0); } });
|
| 198 |
+
proc.on('error', () => { if (!resolved) { resolved = true; clearTimeout(timeout); resolve(false); } });
|
| 199 |
+
});
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
// --- Strategy 1: yt-dlp ---
|
| 203 |
+
function fetchWithYtdlp(ytdlpCmd: string, url: string, videoId: string, emitProgress: ProgressEmitter): Promise<ToolResult> {
|
| 204 |
+
return new Promise((resolve, reject) => {
|
| 205 |
+
const outTemplate = path.join(OUTPUT_DIR, videoId);
|
| 206 |
+
|
| 207 |
+
// Build the full command string
|
| 208 |
+
const cmdLine = `${ytdlpCmd} --write-auto-sub --write-sub --sub-lang en,en-US,en-GB --skip-download --sub-format vtt/srt/best -o "${outTemplate}" "${url}"`;
|
| 209 |
+
|
| 210 |
+
const child = spawn(cmdLine, [], { shell: true });
|
| 211 |
+
let stderr = '';
|
| 212 |
+
|
| 213 |
+
child.stdout?.on('data', (chunk: Buffer) => emitProgress(chunk.toString().trim()));
|
| 214 |
+
child.stderr?.on('data', (chunk: Buffer) => { stderr += chunk.toString(); });
|
| 215 |
+
|
| 216 |
+
child.on('close', (code) => {
|
| 217 |
+
if (code !== 0) return reject(new Error(`yt-dlp exited ${code}: ${stderr}`));
|
| 218 |
+
|
| 219 |
+
// Find generated subtitle files
|
| 220 |
+
const files = fs.readdirSync(OUTPUT_DIR).filter((f) =>
|
| 221 |
+
f.startsWith(videoId) && (f.endsWith('.vtt') || f.endsWith('.srt'))
|
| 222 |
+
);
|
| 223 |
+
if (!files.length) return reject(new Error('No subtitle file generated.'));
|
| 224 |
+
|
| 225 |
+
const subContent = fs.readFileSync(path.join(OUTPUT_DIR, files[0]), 'utf-8');
|
| 226 |
+
const text = files[0].endsWith('.srt') ? parseSrt(subContent) : parseVtt(subContent);
|
| 227 |
+
const fname = `${videoId}-transcript.txt`;
|
| 228 |
+
fs.writeFileSync(path.join(OUTPUT_DIR, fname), text, 'utf-8');
|
| 229 |
+
|
| 230 |
+
emitProgress(`Transcript saved: ${fname} (${text.length} chars)`);
|
| 231 |
+
resolve({ transcript: text, downloadUrl: `/api/download/${fname}`, filename: fname, method: 'yt-dlp' });
|
| 232 |
+
});
|
| 233 |
+
|
| 234 |
+
child.on('error', reject);
|
| 235 |
+
});
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
// --- Strategy 2: YouTube Innertube API ---
|
| 239 |
+
async function fetchWithInnertube(videoId: string, emitProgress: ProgressEmitter): Promise<ToolResult> {
|
| 240 |
+
emitProgress('Fetching via YouTube Innertube API...');
|
| 241 |
+
|
| 242 |
+
const body = JSON.stringify({
|
| 243 |
+
context: {
|
| 244 |
+
client: {
|
| 245 |
+
clientName: 'WEB',
|
| 246 |
+
clientVersion: '2.20240101.00.00',
|
| 247 |
+
hl: 'en',
|
| 248 |
+
gl: 'US',
|
| 249 |
+
},
|
| 250 |
+
},
|
| 251 |
+
videoId: videoId,
|
| 252 |
+
});
|
| 253 |
+
|
| 254 |
+
const responseText = await httpPost(
|
| 255 |
+
'https://www.youtube.com/youtubei/v1/get_transcript?prettyPrint=false',
|
| 256 |
+
body,
|
| 257 |
+
{ 'Content-Type': 'application/json' }
|
| 258 |
+
);
|
| 259 |
+
|
| 260 |
+
// Parse the innertube transcript response
|
| 261 |
+
const lines: string[] = [];
|
| 262 |
+
try {
|
| 263 |
+
const data = JSON.parse(responseText);
|
| 264 |
+
const actions = data?.actions;
|
| 265 |
+
if (actions) {
|
| 266 |
+
for (const action of actions) {
|
| 267 |
+
const segments = action?.updateEngagementPanelAction?.content?.transcriptRenderer
|
| 268 |
+
?.body?.transcriptBodyRenderer?.cueGroups;
|
| 269 |
+
if (segments) {
|
| 270 |
+
for (const seg of segments) {
|
| 271 |
+
const cues = seg?.transcriptCueGroupRenderer?.cues;
|
| 272 |
+
if (cues) {
|
| 273 |
+
for (const cue of cues) {
|
| 274 |
+
const text = cue?.transcriptCueRenderer?.cue?.simpleText;
|
| 275 |
+
if (text) lines.push(text.trim());
|
| 276 |
+
}
|
| 277 |
+
}
|
| 278 |
+
}
|
| 279 |
+
}
|
| 280 |
+
}
|
| 281 |
+
}
|
| 282 |
+
} catch {
|
| 283 |
+
// JSON parse failed
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
if (lines.length === 0) {
|
| 287 |
+
throw new Error('Innertube returned no transcript data.');
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
const text = lines.join('\n');
|
| 291 |
+
const fname = `${videoId}-transcript.txt`;
|
| 292 |
+
fs.writeFileSync(path.join(OUTPUT_DIR, fname), text, 'utf-8');
|
| 293 |
+
emitProgress(`Transcript saved: ${fname} (${text.length} chars, ${lines.length} lines)`);
|
| 294 |
+
|
| 295 |
+
return { transcript: text, downloadUrl: `/api/download/${fname}`, filename: fname, method: 'innertube' };
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
// --- Strategy 3: Page scrape for captionTracks ---
|
| 299 |
+
async function fetchFromPage(videoId: string, emitProgress: ProgressEmitter): Promise<ToolResult> {
|
| 300 |
+
emitProgress('Fetching YouTube page for caption tracks...');
|
| 301 |
+
|
| 302 |
+
const html = await httpGet(`https://www.youtube.com/watch?v=${videoId}`, {
|
| 303 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
| 304 |
+
'Accept-Language': 'en-US,en;q=0.9',
|
| 305 |
+
'Cookie': 'CONSENT=YES+cb.20210328-17-p0.en+FX+999',
|
| 306 |
+
});
|
| 307 |
+
|
| 308 |
+
if (!html || html.length < 1000) {
|
| 309 |
+
throw new Error('YouTube returned empty or blocked page.');
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
// Try multiple regex patterns for caption tracks
|
| 313 |
+
const patterns = [
|
| 314 |
+
/"captionTracks"\s*:\s*(\[.*?\])/s,
|
| 315 |
+
/captionTracks.*?(\[.*?\])/s,
|
| 316 |
+
/"playerCaptionsTracklistRenderer"\s*:\s*\{.*?"captionTracks"\s*:\s*(\[.*?\])/s,
|
| 317 |
+
];
|
| 318 |
+
|
| 319 |
+
let tracks: any[] | null = null;
|
| 320 |
+
for (const pattern of patterns) {
|
| 321 |
+
const m = html.match(pattern);
|
| 322 |
+
if (m) {
|
| 323 |
+
try {
|
| 324 |
+
tracks = JSON.parse(m[1]);
|
| 325 |
+
break;
|
| 326 |
+
} catch {
|
| 327 |
+
continue;
|
| 328 |
+
}
|
| 329 |
+
}
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
if (!tracks || tracks.length === 0) {
|
| 333 |
+
throw new Error('No caption tracks found in page HTML.');
|
| 334 |
+
}
|
| 335 |
+
|
| 336 |
+
// Prefer English captions
|
| 337 |
+
const enTrack =
|
| 338 |
+
tracks.find((t: any) => t.languageCode === 'en' && !t.kind) ||
|
| 339 |
+
tracks.find((t: any) => t.languageCode === 'en') ||
|
| 340 |
+
tracks.find((t: any) => t.languageCode?.startsWith('en')) ||
|
| 341 |
+
tracks[0];
|
| 342 |
+
|
| 343 |
+
if (!enTrack?.baseUrl) {
|
| 344 |
+
throw new Error('No usable caption track URL.');
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
emitProgress(`Found captions: ${enTrack.name?.simpleText || enTrack.languageCode} (${enTrack.kind || 'manual'})`);
|
| 348 |
+
|
| 349 |
+
// Fetch the captions XML -- add fmt=json3 for structured data
|
| 350 |
+
let text = '';
|
| 351 |
+
try {
|
| 352 |
+
const json3Url = enTrack.baseUrl + (enTrack.baseUrl.includes('?') ? '&' : '?') + 'fmt=json3';
|
| 353 |
+
const json3Response = await httpGet(json3Url, { 'User-Agent': 'Mozilla/5.0' });
|
| 354 |
+
text = parseJson3Captions(json3Response);
|
| 355 |
+
} catch {
|
| 356 |
+
// fallback to XML
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
if (!text) {
|
| 360 |
+
const xmlResponse = await httpGet(enTrack.baseUrl, { 'User-Agent': 'Mozilla/5.0' });
|
| 361 |
+
text = parseXmlCaptions(xmlResponse);
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
if (!text.trim()) {
|
| 365 |
+
throw new Error('Caption content is empty after parsing.');
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
const fname = `${videoId}-transcript.txt`;
|
| 369 |
+
fs.writeFileSync(path.join(OUTPUT_DIR, fname), text, 'utf-8');
|
| 370 |
+
emitProgress(`Transcript saved: ${fname} (${text.length} chars)`);
|
| 371 |
+
|
| 372 |
+
return { transcript: text, downloadUrl: `/api/download/${fname}`, filename: fname, method: 'page-scrape' };
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
// --- HTTP helpers ---
|
| 376 |
+
function httpGet(url: string, headers: Record<string, string> = {}): Promise<string> {
|
| 377 |
+
return new Promise((resolve, reject) => {
|
| 378 |
+
const client = url.startsWith('https') ? https : http;
|
| 379 |
+
const parsed = new URL(url);
|
| 380 |
+
const opts = {
|
| 381 |
+
hostname: parsed.hostname,
|
| 382 |
+
port: parsed.port,
|
| 383 |
+
path: parsed.pathname + parsed.search,
|
| 384 |
+
method: 'GET',
|
| 385 |
+
headers: {
|
| 386 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
| 387 |
+
...headers,
|
| 388 |
+
},
|
| 389 |
+
};
|
| 390 |
+
|
| 391 |
+
const req = client.request(opts, (res) => {
|
| 392 |
+
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
| 393 |
+
return httpGet(res.headers.location, headers).then(resolve).catch(reject);
|
| 394 |
+
}
|
| 395 |
+
let data = '';
|
| 396 |
+
res.on('data', (c: Buffer) => { data += c; });
|
| 397 |
+
res.on('end', () => resolve(data));
|
| 398 |
+
res.on('error', reject);
|
| 399 |
+
});
|
| 400 |
+
req.on('error', reject);
|
| 401 |
+
req.end();
|
| 402 |
+
});
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
function httpPost(url: string, body: string, headers: Record<string, string> = {}): Promise<string> {
|
| 406 |
+
return new Promise((resolve, reject) => {
|
| 407 |
+
const parsed = new URL(url);
|
| 408 |
+
const opts = {
|
| 409 |
+
hostname: parsed.hostname,
|
| 410 |
+
port: parsed.port || 443,
|
| 411 |
+
path: parsed.pathname + parsed.search,
|
| 412 |
+
method: 'POST',
|
| 413 |
+
headers: {
|
| 414 |
+
'Content-Type': 'application/json',
|
| 415 |
+
'Content-Length': Buffer.byteLength(body),
|
| 416 |
+
'User-Agent': 'Mozilla/5.0',
|
| 417 |
+
...headers,
|
| 418 |
+
},
|
| 419 |
+
};
|
| 420 |
+
|
| 421 |
+
const req = https.request(opts, (res) => {
|
| 422 |
+
let data = '';
|
| 423 |
+
res.on('data', (c: Buffer) => { data += c; });
|
| 424 |
+
res.on('end', () => resolve(data));
|
| 425 |
+
res.on('error', reject);
|
| 426 |
+
});
|
| 427 |
+
req.on('error', reject);
|
| 428 |
+
req.write(body);
|
| 429 |
+
req.end();
|
| 430 |
+
});
|
| 431 |
+
}
|
| 432 |
+
|
| 433 |
+
// --- Parsers ---
|
| 434 |
+
function parseVtt(vtt: string): string {
|
| 435 |
+
const seen = new Set<string>();
|
| 436 |
+
return vtt.split('\n')
|
| 437 |
+
.map((l) => l.trim())
|
| 438 |
+
.filter((l) => l && l !== 'WEBVTT' && !l.includes('-->') && !/^\d+$/.test(l) && !l.startsWith('Kind:') && !l.startsWith('Language:') && !l.startsWith('NOTE'))
|
| 439 |
+
.map((l) => l.replace(/<[^>]+>/g, '').replace(/ /g, ' ').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').trim())
|
| 440 |
+
.filter((l) => { if (l && !seen.has(l)) { seen.add(l); return true; } return false; })
|
| 441 |
+
.join('\n');
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
function parseSrt(srt: string): string {
|
| 445 |
+
const seen = new Set<string>();
|
| 446 |
+
return srt.split('\n')
|
| 447 |
+
.map((l) => l.trim())
|
| 448 |
+
.filter((l) => l && !l.includes('-->') && !/^\d+$/.test(l))
|
| 449 |
+
.map((l) => l.replace(/<[^>]+>/g, '').trim())
|
| 450 |
+
.filter((l) => { if (l && !seen.has(l)) { seen.add(l); return true; } return false; })
|
| 451 |
+
.join('\n');
|
| 452 |
+
}
|
| 453 |
+
|
| 454 |
+
function parseXmlCaptions(xml: string): string {
|
| 455 |
+
const lines: string[] = [];
|
| 456 |
+
const re = /<text[^>]*>([\s\S]*?)<\/text>/g;
|
| 457 |
+
let m: RegExpExecArray | null;
|
| 458 |
+
while ((m = re.exec(xml)) !== null) {
|
| 459 |
+
const t = decodeEntities(m[1]).replace(/<[^>]+>/g, '').trim();
|
| 460 |
+
if (t) lines.push(t);
|
| 461 |
+
}
|
| 462 |
+
return lines.join('\n');
|
| 463 |
+
}
|
| 464 |
+
|
| 465 |
+
function parseJson3Captions(json: string): string {
|
| 466 |
+
try {
|
| 467 |
+
const data = JSON.parse(json);
|
| 468 |
+
const events = data?.events;
|
| 469 |
+
if (!events) return '';
|
| 470 |
+
|
| 471 |
+
const lines: string[] = [];
|
| 472 |
+
for (const event of events) {
|
| 473 |
+
if (event.segs) {
|
| 474 |
+
const text = event.segs.map((s: any) => s.utf8 || '').join('').trim();
|
| 475 |
+
if (text && text !== '\n') lines.push(text);
|
| 476 |
+
}
|
| 477 |
+
}
|
| 478 |
+
return lines.join('\n');
|
| 479 |
+
} catch {
|
| 480 |
+
return '';
|
| 481 |
+
}
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
function decodeEntities(str: string): string {
|
| 485 |
+
return str
|
| 486 |
+
.replace(/&/g, '&')
|
| 487 |
+
.replace(/</g, '<')
|
| 488 |
+
.replace(/>/g, '>')
|
| 489 |
+
.replace(/"/g, '"')
|
| 490 |
+
.replace(/'/g, "'")
|
| 491 |
+
.replace(/'/g, "'")
|
| 492 |
+
.replace(/&#(\d+);/g, (_, num) => String.fromCharCode(parseInt(num, 10)));
|
| 493 |
+
}
|
tsconfig.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "ES2022",
|
| 4 |
+
"module": "commonjs",
|
| 5 |
+
"lib": [
|
| 6 |
+
"ES2022"
|
| 7 |
+
],
|
| 8 |
+
"outDir": "./dist",
|
| 9 |
+
"rootDir": ".",
|
| 10 |
+
"strict": true,
|
| 11 |
+
"esModuleInterop": true,
|
| 12 |
+
"skipLibCheck": true,
|
| 13 |
+
"forceConsistentCasingInFileNames": true,
|
| 14 |
+
"resolveJsonModule": true,
|
| 15 |
+
"declaration": true
|
| 16 |
+
},
|
| 17 |
+
"include": [
|
| 18 |
+
"server/**/*.ts"
|
| 19 |
+
],
|
| 20 |
+
"exclude": [
|
| 21 |
+
"node_modules",
|
| 22 |
+
"client",
|
| 23 |
+
"dist"
|
| 24 |
+
]
|
| 25 |
+
}
|