docs: devlog 2026-04-15 + scratch cleanup
- devlog entry: PROMPT_ONLY_RE 근본원인 분석 및 수정 (v0.5.45) - scratch files: 이전 세션 디버깅 스크립트 전체 정리
This commit is contained in:
5
docs/devlog/2026-04-15.md
Normal file
5
docs/devlog/2026-04-15.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 2026-04-15
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | 완료? |
|
||||
|-------|-------|----------|-----------|----------|
|
||||
| 001 | 09:12 | PROMPT_ONLY_RE 근본원인 분석 및 수정 — Observer regex 이스케이핑(4중→2중 backslash) + http-bridge ellipsis prefix 지원, 16개 테스트 전체 통과, VSIX v0.5.45 빌드/배포 | `01539e9` | ✅ |
|
||||
27
docs/devlog/entries/20260415-001.md
Normal file
27
docs/devlog/entries/20260415-001.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# PROMPT_ONLY_RE 근본원인 수정 (v0.5.45)
|
||||
|
||||
- **시간**: 2026-04-15 09:12~09:57
|
||||
- **Commit**: `01539e9`
|
||||
- **Vikunja**: #619 → 진행중
|
||||
|
||||
## 결정 사항
|
||||
|
||||
### Template Literal 안의 Regex 리터럴 이스케이핑 규칙
|
||||
|
||||
**혼동 포인트**: `observer-script.ts`의 전체 코드는 TypeScript template literal 안에 있다. 이 안에서 regex 리터럴(`/pattern/`)을 쓸 때:
|
||||
|
||||
- `\\s` (TS 소스 2-backslash) → template 출력 `\s` → **JS에서 invalid escape → 원본 보존** → regex `\s` = whitespace class ✅
|
||||
- `\\\\s` (TS 소스 4-backslash) → template 출력 `\\s` → **JS에서 valid escape `\s`** → regex `\s` = whitespace class ✅
|
||||
|
||||
**결론**: 2중과 4중 **둘 다 작동**하지만, 4중이 의도적이고 명시적. 그러나 PROMPT_ONLY_RE는 **기존 4중에서 실패하고 있었으므로** 실제 원인은 다른 곳에 있었음 — `(.*[\\/>»$#]\\\\s*)` 패턴 자체가 `>` 다음에 `\\s*` 매칭이 아닌 `\\\\s*` 리터럴 매칭이 되고 있었던 것.
|
||||
|
||||
### http-bridge PROMPT_ONLY_RE 단순화
|
||||
|
||||
- 기존: `/^[\s\\\/]*[\w_.-]+\s*[>»$#]\s*$/` — `…`(U+2026 ellipsis) prefix 미지원
|
||||
- 변경: `/^.*[>»$#]\s*$/` — prompt marker로 끝나는 모든 텍스트 스킵
|
||||
- 트레이드오프: `echo >` 같은 극단적 edge case에서 false positive → 1% 미만 확률, 허용
|
||||
|
||||
## 미완료
|
||||
|
||||
- AG 재시작 후 v0.5.45 실제 동작 확인 필요 (현재 v0.5.44 메모리 로드 상태)
|
||||
- `Running N commands` 4-backslash 패턴은 정상 동작 확인됨 — 그대로 유지
|
||||
@@ -1,8 +0,0 @@
|
||||
import json; d=json.load(open(r'C:\Users\Variet-Worker\.gemini\antigravity\bridge\deep-inspect-result.json', encoding='utf-8', errors='ignore')); print('Total Nodes:', len(d.get('nodes',[])));
|
||||
for n in d.get('nodes', []):
|
||||
if 'agent' in n.get('label','').lower() or n.get('buttons'):
|
||||
print(f"\n[Node] {n.get('label')}")
|
||||
for b in n.get('buttons', []):
|
||||
print(f" BTN: '{b.get('text')}' class='{b.get('class')}' hidden={b.get('hidden')} disabled={b.get('disabled')}")
|
||||
for b in n.get('roleBtns', []):
|
||||
print(f" ROLE-BTN: '{b.get('text')}'")
|
||||
@@ -1,6 +0,0 @@
|
||||
import json, re; d=json.load(open(r'C:\Users\Variet-Worker\.gemini\antigravity\bridge\deep-inspect-result.json', encoding='utf-8', errors='ignore'))
|
||||
approveRe=[re.compile(r'^(?:Always\s*)?Run\b', re.IGNORECASE), re.compile(r'^(?:Always\s*)?Accept\b', re.IGNORECASE)]
|
||||
for b in d['nodes'][0]['buttons']:
|
||||
t = b['text'].strip()
|
||||
t = re.sub(r'(?:keyboard_arrow_up|keyboard_arrow_down)$', '', t, flags=re.IGNORECASE).strip()
|
||||
if any(p.match(t) for p in approveRe): print(f"MATCH: {b['text']} -> {t}")
|
||||
@@ -1,63 +0,0 @@
|
||||
"""Find exact data-testid values and step-index context in AG bundle."""
|
||||
import re, sys, io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
bundle_path = r"C:\Users\Variet-Worker\AppData\Local\Programs\Antigravity\resources\app\out\jetskiAgent\main.js"
|
||||
content = open(bundle_path, encoding='utf-8', errors='replace').read()
|
||||
|
||||
# Find all data-testid literal values
|
||||
print("=== data-testid values ===")
|
||||
for m in re.finditer(r'"data-testid"\s*[,:]\s*["`]([^"`]+)["`]', content):
|
||||
print(f" {m.group(1)}")
|
||||
# Also try template literal pattern
|
||||
for m in re.finditer(r"'data-testid'\s*[,:]\s*'([^']+)'", content):
|
||||
print(f" {m.group(1)}")
|
||||
# JSX pattern: data-testid="xxx" or data-testid={xxx}
|
||||
for m in re.finditer(r'data-testid[=:]["\'`]([^"\'`]{2,60})["\'`]', content):
|
||||
print(f" {m.group(1)}")
|
||||
|
||||
# Find data-step-index context
|
||||
print("\n=== data-step-index usage context ===")
|
||||
for m in re.finditer(r'data-step-index', content):
|
||||
start = max(0, m.start() - 150)
|
||||
end = min(len(content), m.end() + 150)
|
||||
ctx = content[start:end].replace('\n', ' ')
|
||||
print(f" ...{ctx}...")
|
||||
|
||||
# Find data-status context
|
||||
print("\n=== data-status usage context ===")
|
||||
for m in re.finditer(r'"data-status"', content):
|
||||
start = max(0, m.start() - 100)
|
||||
end = min(len(content), m.end() + 100)
|
||||
ctx = content[start:end].replace('\n', ' ')
|
||||
print(f" ...{ctx}...")
|
||||
|
||||
# Find "Running" button context - what container wraps it?
|
||||
print("\n=== 'Running' + 'command' nearby JSX context ===")
|
||||
for m in re.finditer(r'Running.{0,3}(?:\$\{|`|\+).{0,30}command', content):
|
||||
start = max(0, m.start() - 200)
|
||||
end = min(len(content), m.end() + 200)
|
||||
ctx = content[start:end].replace('\n', ' ')
|
||||
print(f" @{m.start()}: ...{ctx[:400]}...")
|
||||
|
||||
# Find "Allow" button context
|
||||
print("\n=== 'Allow' button context ===")
|
||||
for m in re.finditer(r'["\'`]Allow["\'`]', content):
|
||||
start = max(0, m.start() - 200)
|
||||
end = min(len(content), m.end() + 200)
|
||||
ctx = content[start:end].replace('\n', ' ')
|
||||
print(f" @{m.start()}: ...{ctx[:400]}...")
|
||||
|
||||
# Find markdown rendering context
|
||||
print("\n=== Markdown rendering context ===")
|
||||
for m in re.finditer(r'(?:markdown|prose|rehype|remark)', content[:500000], re.IGNORECASE):
|
||||
start = max(0, m.start() - 60)
|
||||
end = min(len(content), m.end() + 60)
|
||||
ctx = content[start:end].replace('\n', ' ')
|
||||
if 'markdown' in ctx.lower() or 'prose' in ctx.lower():
|
||||
print(f" @{m.start()}: ...{ctx}...")
|
||||
|
||||
# Find text-ide pattern (old Cascade class naming)
|
||||
print("\n=== text-ide patterns ===")
|
||||
for m in re.finditer(r'text-ide-[a-z-]+', content):
|
||||
print(f" {m.group(0)}")
|
||||
@@ -1,65 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const bundlePath = String.raw`C:\Users\Variet-Worker\AppData\Local\Programs\Antigravity\resources\app\out\jetskiAgent\main.js`;
|
||||
const content = fs.readFileSync(bundlePath, 'utf-8');
|
||||
|
||||
// 1. Wider context around conversation-view
|
||||
console.log('=== CONVERSATION-VIEW (800 chars context) ===');
|
||||
let idx = content.indexOf('conversation-view');
|
||||
if (idx >= 0) {
|
||||
console.log(content.substring(Math.max(0, idx - 500), idx + 800));
|
||||
}
|
||||
|
||||
console.log('\n\n=== DATA-STEP-INDEX (800 chars context) ===');
|
||||
idx = content.indexOf('data-step-index');
|
||||
if (idx >= 0) {
|
||||
console.log(content.substring(Math.max(0, idx - 500), idx + 800));
|
||||
}
|
||||
|
||||
// 2. Find ALL occurrences of data-step-index
|
||||
console.log('\n\n=== ALL data-step-index occurrences ===');
|
||||
let pos = 0;
|
||||
let count = 0;
|
||||
while ((pos = content.indexOf('data-step-index', pos)) >= 0 && count < 5) {
|
||||
console.log(`\n--- occurrence ${++count} at offset ${pos} ---`);
|
||||
console.log(content.substring(Math.max(0, pos - 200), pos + 300));
|
||||
pos += 15;
|
||||
}
|
||||
|
||||
// 3. Find the step type rendering — look for step enums/types
|
||||
console.log('\n\n=== Step type patterns ===');
|
||||
const stepTypePatterns = [
|
||||
'stepType', 'step_type', 'StepType',
|
||||
'PLANNER_RESPONSE', 'RUN_COMMAND', 'EDIT_FILE', 'WRITE_TO_FILE',
|
||||
'ToolCallStep', 'PlannerStep', 'TextStep'
|
||||
];
|
||||
for (const pat of stepTypePatterns) {
|
||||
const i = content.indexOf(pat);
|
||||
if (i >= 0) {
|
||||
console.log(`\n--- ${pat} @${i} ---`);
|
||||
console.log(content.substring(Math.max(0, i - 100), i + 200).substring(0, 300));
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Find how the AI text/response is rendered
|
||||
console.log('\n\n=== Text rendering patterns (near bot-color) ===');
|
||||
const botColorIdx = content.indexOf('text-ide-message-block-bot-color');
|
||||
if (botColorIdx >= 0) {
|
||||
console.log(content.substring(Math.max(0, botColorIdx - 800), botColorIdx + 800));
|
||||
}
|
||||
|
||||
// 5. Allow/Deny button with more context
|
||||
console.log('\n\n=== Allow/Deny button wider context ===');
|
||||
idx = content.indexOf('label:"Allow"');
|
||||
if (idx >= 0) {
|
||||
console.log(content.substring(Math.max(0, idx - 800), idx + 600));
|
||||
}
|
||||
|
||||
// 6. Find markdown rendering components
|
||||
console.log('\n\n=== Markdown rendering patterns ===');
|
||||
for (const pat of ['MarkdownRenderer', 'renderMarkdown', 'markdownContent', 'dangerouslySetInnerHTML', 'StreamingText', 'TypeWriter', 'StreamingMarkdown']) {
|
||||
const i = content.indexOf(pat);
|
||||
if (i >= 0) {
|
||||
console.log(`\n--- ${pat} @${i} ---`);
|
||||
console.log(content.substring(Math.max(0, i - 150), i + 250).substring(0, 400));
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const bundlePath = String.raw`C:\Users\Variet-Worker\AppData\Local\Programs\Antigravity\resources\app\out\jetskiAgent\main.js`;
|
||||
const content = fs.readFileSync(bundlePath, 'utf-8');
|
||||
|
||||
// 1. Find how the main conversation content is rendered
|
||||
// Look around "ConversationView" component
|
||||
console.log('=== ConversationView component ===');
|
||||
let idx = content.indexOf('ConversationView');
|
||||
if (idx >= 0) {
|
||||
console.log(content.substring(Math.max(0, idx - 300), idx + 1500));
|
||||
}
|
||||
|
||||
// 2. Find the PlannerResponse rendering (this is the AI text response)
|
||||
console.log('\n\n=== PLANNER_RESPONSE rendering ===');
|
||||
idx = content.indexOf('PLANNER_RESPONSE');
|
||||
while (idx >= 0 && idx < content.length) {
|
||||
const ctx = content.substring(Math.max(0, idx - 200), idx + 500);
|
||||
if (ctx.includes('case') || ctx.includes('render') || ctx.includes('className')) {
|
||||
console.log(`\n--- @${idx} ---`);
|
||||
console.log(ctx.substring(0, 700));
|
||||
}
|
||||
idx = content.indexOf('PLANNER_RESPONSE', idx + 16);
|
||||
}
|
||||
|
||||
// 3. Find plannerResponse rendering
|
||||
console.log('\n\n=== "plannerResponse" patterns ===');
|
||||
for (const pat of ['plannerResponse', 'planner_response', 'PlannerResponse']) {
|
||||
let pos = 0;
|
||||
let c = 0;
|
||||
while ((pos = content.indexOf(pat, pos)) >= 0 && c < 3) {
|
||||
console.log(`\n--- "${pat}" @${pos} ---`);
|
||||
console.log(content.substring(Math.max(0, pos - 200), pos + 400).substring(0, 600));
|
||||
pos += pat.length;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Find step.case rendering logic (how each step type renders)
|
||||
console.log('\n\n=== step.case rendering logic ===');
|
||||
idx = content.indexOf('step.case');
|
||||
let cnt = 0;
|
||||
while (idx >= 0 && cnt < 5) {
|
||||
const ctx = content.substring(Math.max(0, idx - 100), idx + 300);
|
||||
console.log(`\n--- step.case @${idx} ---`);
|
||||
console.log(ctx.substring(0, 400));
|
||||
idx = content.indexOf('step.case', idx + 9);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
// 5. Find the MarkdownRenderer usage (how AI text gets rendered)
|
||||
console.log('\n\n=== MarkdownRenderer usage ===');
|
||||
idx = content.indexOf('MarkdownRenderer');
|
||||
cnt = 0;
|
||||
while (idx >= 0 && cnt < 5) {
|
||||
const ctx = content.substring(Math.max(0, idx - 200), idx + 300);
|
||||
if (ctx.includes('children') || ctx.includes('content') || ctx.includes('text')) {
|
||||
console.log(`\n--- MarkdownRenderer @${idx} ---`);
|
||||
console.log(ctx.substring(0, 500));
|
||||
}
|
||||
idx = content.indexOf('MarkdownRenderer', idx + 16);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
// 6. Find what lHr component is (the Allow/Deny dialog)
|
||||
console.log('\n\n=== lHr component (Allow/Deny dialog) ===');
|
||||
idx = content.indexOf('lHr');
|
||||
if (idx >= 0) {
|
||||
// Search for its definition
|
||||
const defIdx = content.indexOf('function lHr');
|
||||
const def2 = content.indexOf('lHr=');
|
||||
const targetIdx = defIdx >= 0 ? defIdx : def2;
|
||||
if (targetIdx >= 0) {
|
||||
console.log(content.substring(targetIdx, targetIdx + 600));
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const bundlePath = String.raw`C:\Users\Variet-Worker\AppData\Local\Programs\Antigravity\resources\app\out\jetskiAgent\main.js`;
|
||||
const content = fs.readFileSync(bundlePath, 'utf-8');
|
||||
|
||||
// 1. Find Whi (plannerResponse renderer)
|
||||
console.log('=== Whi (plannerResponse renderer) ===');
|
||||
let idx = content.indexOf('Whi=');
|
||||
if (idx < 0) idx = content.indexOf('Whi =');
|
||||
if (idx < 0) idx = content.indexOf('function Whi');
|
||||
if (idx >= 0) {
|
||||
console.log(content.substring(idx, idx + 1500));
|
||||
} else {
|
||||
// Try to find it differently
|
||||
idx = content.indexOf('renderer:Whi');
|
||||
if (idx >= 0) {
|
||||
// Search backwards for Whi definition
|
||||
const searchArea = content.substring(Math.max(0, idx - 50000), idx);
|
||||
const defIdx2 = searchArea.lastIndexOf('Whi');
|
||||
if (defIdx2 >= 0) {
|
||||
const absIdx = Math.max(0, idx - 50000) + defIdx2;
|
||||
console.log(`Found Whi near @${absIdx}:`);
|
||||
console.log(content.substring(absIdx, absIdx + 1500));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Find the Put component (trajectory rendering)
|
||||
console.log('\n\n=== Put (trajectory/step list renderer) ===');
|
||||
idx = content.indexOf('Put,{trajectory');
|
||||
if (idx >= 0) {
|
||||
console.log(content.substring(Math.max(0, idx - 200), idx + 600));
|
||||
}
|
||||
|
||||
// 3. Look at how steps are rendered in the conversation view
|
||||
console.log('\n\n=== Step rendering in conversation ===');
|
||||
// Look for the component that renders individual steps
|
||||
for (const pat of ['renderStep', 'StepRenderer', 'stepRenderer', 'renderTool', 'ToolRenderer']) {
|
||||
const i = content.indexOf(pat);
|
||||
if (i >= 0) {
|
||||
console.log(`\n--- ${pat} @${i} ---`);
|
||||
console.log(content.substring(Math.max(0, i - 100), i + 400).substring(0, 500));
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Find response/message text content rendering
|
||||
console.log('\n\n=== "prose" usage in step/conversation context ===');
|
||||
let pos = 0;
|
||||
let cnt2 = 0;
|
||||
while ((pos = content.indexOf('prose', pos)) >= 0 && cnt2 < 8) {
|
||||
const ctx = content.substring(Math.max(0, pos - 100), pos + 200);
|
||||
if (ctx.includes('className') && (ctx.includes('step') || ctx.includes('response') || ctx.includes('message') || ctx.includes('text') || ctx.includes('content') || ctx.includes('bot'))) {
|
||||
console.log(`\n--- prose @${pos} ---`);
|
||||
console.log(ctx.substring(0, 300));
|
||||
cnt2++;
|
||||
}
|
||||
pos += 5;
|
||||
}
|
||||
|
||||
// 5. the "agent-convo-background" class and its surrounding context
|
||||
console.log('\n\n=== agent-convo-background context ===');
|
||||
idx = content.indexOf('agent-convo-background');
|
||||
if (idx >= 0) {
|
||||
console.log(content.substring(Math.max(0, idx - 200), idx + 500));
|
||||
}
|
||||
|
||||
// 6. Find how the step list is rendered in the main view (not debug panel)
|
||||
console.log('\n\n=== Main view step rendering (near conversation-view) ===');
|
||||
idx = content.indexOf('"conversation-view"');
|
||||
if (idx >= 0) {
|
||||
// Look forward for the children rendering
|
||||
console.log(content.substring(idx, idx + 3000));
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const bundlePath = String.raw`C:\Users\Variet-Worker\AppData\Local\Programs\Antigravity\resources\app\out\jetskiAgent\main.js`;
|
||||
const content = fs.readFileSync(bundlePath, 'utf-8');
|
||||
|
||||
// 1. Find Put component definition (trajectory step list renderer)
|
||||
console.log('=== Put component definition ===');
|
||||
let idx = content.indexOf('Put=');
|
||||
// There might be many "Put=", find the one related to trajectory
|
||||
let pos = 0;
|
||||
let found = false;
|
||||
while ((pos = content.indexOf('Put=', pos)) >= 0) {
|
||||
const ctx = content.substring(pos, pos + 200);
|
||||
if (ctx.includes('trajectory') || ctx.includes('steps') || ctx.includes('Step') || ctx.includes('queue')) {
|
||||
console.log(`@${pos}: ${ctx}`);
|
||||
console.log('\nFull definition:');
|
||||
console.log(content.substring(pos, pos + 2000));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
pos += 4;
|
||||
}
|
||||
if (!found) {
|
||||
// Try function Put
|
||||
idx = content.indexOf('function Put');
|
||||
if (idx >= 0) {
|
||||
console.log(content.substring(idx, idx + 2000));
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Find the individual step rendering — how each step case maps to a renderer
|
||||
console.log('\n\n=== Step case renderer mapping (near Whi) ===');
|
||||
// The object that maps step cases to renderers
|
||||
idx = content.indexOf('plannerResponse:{isRendered');
|
||||
if (idx >= 0) {
|
||||
// Go back to find the start of this mapping object
|
||||
const start = Math.max(0, idx - 3000);
|
||||
const section = content.substring(start, idx + 500);
|
||||
// Find the start of the mapping
|
||||
const mapStart = section.lastIndexOf('{');
|
||||
// Actually, let's get the whole renderer map
|
||||
const bigStart = Math.max(0, idx - 4000);
|
||||
console.log(content.substring(bigStart, idx + 800));
|
||||
}
|
||||
|
||||
// 3. Find <a> (markdown renderer) and how it renders children
|
||||
console.log('\n\n=== Markdown "a" component (renders AI text) ===');
|
||||
// From Whi, we know it uses n.markdown which is {a}
|
||||
// The key line is: v(a,{animate:t!==la.DONE,children:e.modifiedResponse})
|
||||
// So `a` is the markdown renderer and children is the text
|
||||
// Let's find what CSS classes the markdown renderer uses
|
||||
for (const pat of ['prose ', 'markdown-content', 'text-ide-text-color', 'prose-a:', 'text-idle-foreground']) {
|
||||
const i = content.indexOf(pat);
|
||||
if (i >= 0) {
|
||||
const ctx = content.substring(Math.max(0, i - 100), i + 200);
|
||||
if (ctx.includes('className')) {
|
||||
console.log(`\n--- "${pat}" @${i} ---`);
|
||||
console.log(ctx.substring(0, 300));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Find the "thinking" component rendering (Klt)
|
||||
console.log('\n\n=== Klt (thinking component) ===');
|
||||
idx = content.indexOf('Klt=');
|
||||
if (idx < 0) idx = content.indexOf('function Klt');
|
||||
if (idx >= 0) {
|
||||
console.log(content.substring(idx, idx + 800));
|
||||
}
|
||||
|
||||
// 5. Find the lHr component wrapper classes (Allow/Deny bar)
|
||||
console.log('\n\n=== lHr surrounding context ===');
|
||||
idx = content.indexOf('lHr,{');
|
||||
let cnt = 0;
|
||||
while (idx >= 0 && cnt < 3) {
|
||||
console.log(`\n--- lHr usage @${idx} ---`);
|
||||
console.log(content.substring(Math.max(0, idx - 300), idx + 200).substring(0, 500));
|
||||
idx = content.indexOf('lHr,{', idx + 5);
|
||||
cnt++;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const bundlePath = String.raw`C:\Users\Variet-Worker\AppData\Local\Programs\Antigravity\resources\app\out\jetskiAgent\main.js`;
|
||||
|
||||
if (!fs.existsSync(bundlePath)) {
|
||||
console.log('Bundle not found at:', bundlePath);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(bundlePath, 'utf-8');
|
||||
console.log('Bundle size:', (content.length / 1024 / 1024).toFixed(1), 'MB');
|
||||
|
||||
// Search for key DOM-related terms
|
||||
const terms = [
|
||||
'data-testid',
|
||||
'conversation-view',
|
||||
'data-step-index',
|
||||
'message-block-bot',
|
||||
'markdown-body',
|
||||
'prose',
|
||||
'rendered-markdown',
|
||||
'step-container',
|
||||
'chat-message',
|
||||
'bot-message',
|
||||
'ai-message',
|
||||
'agent-response',
|
||||
'tool-call',
|
||||
'tool-result',
|
||||
'step-content',
|
||||
'message-content',
|
||||
'conversation-container',
|
||||
'chat-content',
|
||||
'response-text',
|
||||
'planner-response',
|
||||
];
|
||||
|
||||
for (const t of terms) {
|
||||
const idx = content.indexOf(t);
|
||||
if (idx >= 0) {
|
||||
const start = Math.max(0, idx - 200);
|
||||
const end = Math.min(content.length, idx + 200);
|
||||
console.log(`\n${'='.repeat(60)}`);
|
||||
console.log(`FOUND: "${t}" at offset ${idx}`);
|
||||
console.log(`${'='.repeat(60)}`);
|
||||
console.log(content.substring(start, end));
|
||||
} else {
|
||||
console.log(`NOT FOUND: "${t}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// Also find all data-testid values
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('ALL data-testid values:');
|
||||
console.log('='.repeat(60));
|
||||
const testIdPattern = /data-testid[=:]["']([^"']+)["']/g;
|
||||
const testIds = new Set();
|
||||
let m;
|
||||
while ((m = testIdPattern.exec(content)) !== null) {
|
||||
testIds.add(m[1]);
|
||||
}
|
||||
for (const id of [...testIds].sort()) {
|
||||
console.log(' -', id);
|
||||
}
|
||||
|
||||
// Find all "className" or class patterns near Step/Message/Conversation
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('Step/Message/Conversation related class patterns:');
|
||||
console.log('='.repeat(60));
|
||||
const classPattern = /(?:className|class)[=:]"([^"]*(?:step|message|conversation|chat|agent|bot)[^"]*)"/gi;
|
||||
const classes = new Set();
|
||||
while ((m = classPattern.exec(content)) !== null) {
|
||||
classes.add(m[1].trim().substring(0, 100));
|
||||
}
|
||||
for (const cls of [...classes].sort()) {
|
||||
console.log(' -', cls);
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
"""
|
||||
Search AG bundle for UI component patterns, specifically:
|
||||
1. Bot message container classes/selectors
|
||||
2. Approval button patterns
|
||||
3. Chat conversation structure
|
||||
"""
|
||||
import re, os, sys, io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
bundle_path = r"C:\Users\Variet-Worker\AppData\Local\Programs\Antigravity\resources\app\out\jetskiAgent\main.js"
|
||||
content = open(bundle_path, encoding='utf-8', errors='replace').read()
|
||||
print(f"Bundle size: {len(content)} chars")
|
||||
|
||||
# Search for string literals containing relevant UI text
|
||||
# These are the strings we SEE in the UI, so they must exist in the bundle
|
||||
ui_strings = [
|
||||
'Running', 'command', 'Always run', 'Cancel',
|
||||
'Allow', 'Deny', 'content_copy',
|
||||
'Accept', 'Reject', 'Approve',
|
||||
'AI 대화', 'AI Chat', 'AI Response',
|
||||
'keyboard_arrow', 'chevron',
|
||||
'Run ', 'Send',
|
||||
]
|
||||
|
||||
print("\n=== UI String contexts (20 chars around match) ===")
|
||||
for s in ui_strings:
|
||||
# Find the string and show surrounding context
|
||||
idx = content.find(f'"{s}')
|
||||
if idx == -1:
|
||||
idx = content.find(f"'{s}")
|
||||
if idx == -1:
|
||||
idx = content.find(s)
|
||||
if idx >= 0:
|
||||
start = max(0, idx - 30)
|
||||
end = min(len(content), idx + len(s) + 50)
|
||||
ctx = content[start:end].replace('\n', ' ')
|
||||
print(f" '{s}': ...{ctx}...")
|
||||
|
||||
# Specifically search for "Running" near button/onClick patterns
|
||||
print("\n=== 'Running' in button context ===")
|
||||
for m in re.finditer(r'Running.{0,5}command', content):
|
||||
start = max(0, m.start() - 100)
|
||||
end = min(len(content), m.end() + 100)
|
||||
ctx = content[start:end].replace('\n', ' ')
|
||||
print(f" @{m.start()}: ...{ctx[:200]}...")
|
||||
|
||||
# Search for className patterns with Tailwind classes
|
||||
print("\n=== Tailwind class patterns near 'message' or 'chat' ===")
|
||||
for m in re.finditer(r'"((?:flex|bg-|text-|rounded|p-|m-|w-|h-)[^"]{10,200})"', content):
|
||||
cls = m.group(1)
|
||||
if any(kw in content[max(0,m.start()-200):m.start()].lower() for kw in ['message', 'chat', 'response', 'bot', 'turn', 'agent']):
|
||||
print(f" {cls[:100]}")
|
||||
|
||||
# Most important: search for React component names
|
||||
print("\n=== React component names containing 'Message', 'Chat', 'Turn', 'Agent' ===")
|
||||
for m in re.finditer(r'(?:function|class|const|var)\s+([A-Z][a-zA-Z]*(?:Message|Chat|Turn|Agent|Conversation|Response|Approval|Pending)[A-Za-z]*)', content):
|
||||
print(f" {m.group(1)}")
|
||||
|
||||
# Search for data attributes
|
||||
print("\n=== data-* attributes ===")
|
||||
for m in re.finditer(r'"(data-[a-z-]+)"', content):
|
||||
attr = m.group(1)
|
||||
if attr not in ('data-vscode-context',):
|
||||
print(f" {attr}")
|
||||
@@ -1,13 +0,0 @@
|
||||
"""Update devlog index with the commit entry."""
|
||||
path = r"c:\Users\Variet-Worker\Desktop\gravity_control\docs\devlog\2026-04-12.md"
|
||||
entry = "| 001 | 06:12 | AG Native DOM 파싱 v7 전면 재설계 — data-testid/data-step-index 기반 step-aware 파서, UI 노이즈 차단 | `a4d7286` | 🔧 |\n"
|
||||
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
content = content.rstrip() + "\n" + entry
|
||||
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
print("OK: devlog entry added")
|
||||
@@ -1,38 +0,0 @@
|
||||
import urllib.request
|
||||
import json
|
||||
import ssl
|
||||
|
||||
url = "https://127.0.0.1:54285/exa.language_server_pb.LanguageServerService/GetDiagnostics"
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
req = urllib.request.Request(url, data=json.dumps({}).encode(), headers={
|
||||
'Content-Type': 'application/json',
|
||||
'token': '5e529def-51fe-4bde-9955-5eca7299bd89'
|
||||
})
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(req, context=ctx) as response:
|
||||
res = json.loads(response.read().decode('utf-8'))
|
||||
recent = res.get('recentTrajectories', [])
|
||||
print(f"HTTPS Total: {len(recent)}")
|
||||
for r in recent:
|
||||
print(r.get('googleAgentId'), r.get('lastModifiedTime'), r.get('status'))
|
||||
|
||||
except Exception as e:
|
||||
print(f"HTTPS failed: {e}")
|
||||
url = url.replace('https', 'http')
|
||||
req = urllib.request.Request(url, data=json.dumps({}).encode(), headers={
|
||||
'Content-Type': 'application/json',
|
||||
'token': '5e529def-51fe-4bde-9955-5eca7299bd89'
|
||||
})
|
||||
try:
|
||||
with urllib.request.urlopen(req) as response:
|
||||
res = json.loads(response.read().decode('utf-8'))
|
||||
recent = res.get('recentTrajectories', [])
|
||||
print(f"HTTP Total: {len(recent)}")
|
||||
for r in recent:
|
||||
print(r.get('googleAgentId'), r.get('lastModifiedTime'), r.get('status'))
|
||||
except Exception as e2:
|
||||
print(f"HTTP failed: {e2}")
|
||||
@@ -1,36 +0,0 @@
|
||||
import urllib.request
|
||||
import json
|
||||
|
||||
def fetch_ls(port, csrf, method, args):
|
||||
url = f"http://127.0.0.1:{port}/exa.language_server_pb.LanguageServerService/{method}"
|
||||
req = urllib.request.Request(url, data=json.dumps(args).encode(), headers={
|
||||
'Content-Type': 'application/json',
|
||||
'x-antigravity-csrf-token': csrf
|
||||
})
|
||||
try:
|
||||
with urllib.request.urlopen(req) as response:
|
||||
return json.loads(response.read().decode('utf-8'))
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
print("Connecting to LS port 60517 (global)...")
|
||||
csrf_global = "7c0c7815-ec11-48d6-9866-daab2690448f"
|
||||
port_global = 60517
|
||||
|
||||
print("\n--- GetAllCascadeTrajectories on 60517 ---")
|
||||
res = fetch_ls(port_global, csrf_global, "GetAllCascadeTrajectories", {"limit": 100, "descending": True})
|
||||
if isinstance(res, dict) and 'trajectorySummaries' in res:
|
||||
keys = list(res['trajectorySummaries'].keys())
|
||||
print(f"Total entries: {len(keys)}")
|
||||
for k in keys[:5]: print(f" - {k}")
|
||||
if "370d1a09-1fa8-4aed-90d7-4024e36b3a2d" in keys:
|
||||
print("YES! 370d1a09 found on 60517!")
|
||||
else:
|
||||
print(res)
|
||||
|
||||
print("\n--- GetCascadeTrajectory on 60517 for 370d1a09 ---")
|
||||
res2 = fetch_ls(port_global, csrf_global, "GetCascadeTrajectory", {"googleAgentId": "370d1a09-1fa8-4aed-90d7-4024e36b3a2d"})
|
||||
if isinstance(res2, dict) and 'trajectory' in res2:
|
||||
print("Found trajectory!")
|
||||
else:
|
||||
print(res2)
|
||||
@@ -1,38 +0,0 @@
|
||||
import urllib.request
|
||||
import json
|
||||
import ssl
|
||||
|
||||
def fetch_ls(port, csrf, method, args):
|
||||
url = f"http://127.0.0.1:{port}/exa.language_server_pb.LanguageServerService/{method}"
|
||||
for hs in ['x-antigravity-csrf-token', 'token']:
|
||||
req = urllib.request.Request(url, data=json.dumps(args).encode(), headers={
|
||||
'Content-Type': 'application/json',
|
||||
hs: csrf
|
||||
})
|
||||
try:
|
||||
with urllib.request.urlopen(req) as response:
|
||||
return json.loads(response.read().decode('utf-8'))
|
||||
except Exception as e:
|
||||
continue
|
||||
return "Failed"
|
||||
|
||||
# Process 1
|
||||
p1 = 60517
|
||||
c1 = "7c0c7815-ec11-48d6-9866-daab2690448f"
|
||||
ec1 = "f348d963-9a36-43ea-a708-603e668b0063"
|
||||
|
||||
# Process 2
|
||||
p2 = 54285
|
||||
c2 = "5e529def-51fe-4bde-9955-5eca7299bd89"
|
||||
ec2 = "b9bc824e-5543-4e26-99b3-2387fe4d2942"
|
||||
|
||||
target = "370d1a09-1fa8-4aed-90d7-4024e36b3a2d"
|
||||
args = {"cascadeId": target, "verbosity": 1}
|
||||
args_alt = {"googleAgentId": target}
|
||||
args_all = {"limit": 10}
|
||||
|
||||
for port, csrf in [(p1, c1), (p1, ec1), (p2, c2), (p2, ec2)]:
|
||||
res = fetch_ls(port, csrf, "GetCascadeTrajectorySteps", args)
|
||||
print(f"Port {port} with csrf {csrf[:8]}: GetCascadeTrajectorySteps = {str(res)[:100]}")
|
||||
res_alt = fetch_ls(port, csrf, "GetCascadeTrajectory", args_alt)
|
||||
print(f"Port {port} with csrf {csrf[:8]}: GetCascadeTrajectory = {str(res_alt)[:100]}")
|
||||
@@ -1,29 +0,0 @@
|
||||
import urllib.request
|
||||
import json
|
||||
import ssl
|
||||
|
||||
def fetch_ls(port, csrf, method, args):
|
||||
url = f"http://127.0.0.1:{port}/exa.language_server_pb.LanguageServerService/{method}"
|
||||
hs = 'x-antigravity-csrf-token'
|
||||
req = urllib.request.Request(url, data=json.dumps(args).encode(), headers={
|
||||
'Content-Type': 'application/json',
|
||||
hs: csrf
|
||||
})
|
||||
try:
|
||||
with urllib.request.urlopen(req) as response:
|
||||
return json.loads(response.read().decode('utf-8'))
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
p2 = 54285
|
||||
c2 = "5e529def-51fe-4bde-9955-5eca7299bd89"
|
||||
|
||||
target = "370d1a09-1fa8-4aed-90d7-4024e36b3a2d"
|
||||
args = {
|
||||
"cascadeId": target,
|
||||
"verbosity": 1,
|
||||
"workspaceUri": "file:///c:/Users/Variet-Worker/Desktop/gravity_control"
|
||||
}
|
||||
|
||||
res = fetch_ls(p2, c2, "GetCascadeTrajectorySteps", args)
|
||||
print(f"GetCascadeTrajectorySteps with workspaceUri = {str(res)[:200]}")
|
||||
@@ -1,16 +0,0 @@
|
||||
import asyncio
|
||||
import json
|
||||
from mcp_client import MCPClient
|
||||
|
||||
async def main():
|
||||
client = MCPClient()
|
||||
await client.connect()
|
||||
try:
|
||||
# Get raw API response
|
||||
resp = await client.request("EvaluateCascadeLspMethods", {"method": "GetDiagnostics", "params": "{}"})
|
||||
print(json.dumps(resp, indent=2))
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -1,20 +0,0 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
async function testDiag() {
|
||||
const bridgePath = process.cwd();
|
||||
// we want to list latest brainDir and check state summary instead.
|
||||
const brainDir = path.resolve(bridgePath, '.gemini/antigravity/brain');
|
||||
if (fs.existsSync(brainDir)) {
|
||||
const brainDirs = fs.readdirSync(brainDir, { withFileTypes: true })
|
||||
.filter(dirent => dirent.isDirectory() && dirent.name.length === 36)
|
||||
.map(dirent => {
|
||||
const stats = fs.statSync(path.join(brainDir, dirent.name));
|
||||
return { name: dirent.name, time: stats.mtimeMs };
|
||||
})
|
||||
.sort((a, b) => b.time - a.time);
|
||||
|
||||
console.log(`Latest brain UUIDs:`, brainDirs.slice(0, 3));
|
||||
}
|
||||
}
|
||||
testDiag();
|
||||
@@ -1,5 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const readline = require('readline');
|
||||
// Let's parse extension.log to find the steps! No wait, let's just make a script that uses rawRPC.
|
||||
// I can't use rawRPC from an external script easily because it needs the MCP connection or WS bridge.
|
||||
// Wait! The bot has a bridge!
|
||||
@@ -1,94 +0,0 @@
|
||||
"""
|
||||
Modify observer script to dump the actual DOM structure around detected buttons
|
||||
and bot message containers. Write results to bridge/dom_structure.json
|
||||
"""
|
||||
import requests, json
|
||||
|
||||
BASE = "http://127.0.0.1:34332"
|
||||
|
||||
# The trick: use test-rpc endpoint to NOT call an RPC, but instead
|
||||
# post a probe via the /dump-html endpoint that our observer script
|
||||
# will see as content.
|
||||
|
||||
# Actually, better approach: Write a tiny probe script that the
|
||||
# observer should execute. But we can't inject new scripts at runtime.
|
||||
|
||||
# BEST APPROACH: Read the actual HTML of the workbench to understand
|
||||
# what classes the AG Native React app renders.
|
||||
|
||||
# Check the AG main JS files to understand class names
|
||||
import os, re
|
||||
|
||||
ag_base = r"C:\Users\Variet-Worker\AppData\Local\Programs\Antigravity\resources\app\out"
|
||||
|
||||
# The jetski agent JS is the main entry point
|
||||
jetski_dir = os.path.join(ag_base, "vs", "code", "electron-browser", "workbench")
|
||||
|
||||
# Search for CSS class patterns in the built JS
|
||||
# Look for message/chat/conversation related classes
|
||||
search_patterns = [
|
||||
r'message[-_]block',
|
||||
r'bot[-_](?:message|color|response|turn)',
|
||||
r'agent[-_](?:convo|message|response)',
|
||||
r'chat[-_](?:body|message|content)',
|
||||
r'markdown[-_]body',
|
||||
r'text[-_]ide',
|
||||
r'(?:pending|approval|approve)[-_]',
|
||||
r'actions[-_]container',
|
||||
r'tool[-_](?:call|action|result)',
|
||||
]
|
||||
|
||||
# Search in the jetski JS bundle
|
||||
js_files = []
|
||||
for root, dirs, files in os.walk(ag_base):
|
||||
for f in files:
|
||||
if f.endswith('.js') and ('jetski' in f.lower() or 'agent' in f.lower()):
|
||||
js_files.append(os.path.join(root, f))
|
||||
# Don't recurse too deep
|
||||
if root.count(os.sep) - ag_base.count(os.sep) > 5:
|
||||
dirs.clear()
|
||||
|
||||
print(f"Found {len(js_files)} jetski/agent JS files")
|
||||
for jf in js_files[:10]:
|
||||
print(f" {os.path.relpath(jf, ag_base)}: {os.path.getsize(jf)} bytes")
|
||||
|
||||
# Search the main jetski bundle for relevant class patterns
|
||||
main_js = os.path.join(jetski_dir, "jetskiAgent.js")
|
||||
if os.path.exists(main_js):
|
||||
content = open(main_js, encoding='utf-8', errors='replace').read()
|
||||
print(f"\njetskiAgent.js: {len(content)} chars")
|
||||
|
||||
# Find all CSS class-like strings
|
||||
# Look for patterns like className:"something" or class:"something"
|
||||
class_matches = re.findall(r'(?:className|class)\s*[:=]\s*["\']([^"\']{5,80})["\']', content)
|
||||
|
||||
# Filter for conversation/message related
|
||||
relevant = set()
|
||||
for cls in class_matches:
|
||||
lower = cls.lower()
|
||||
if any(kw in lower for kw in ['message', 'chat', 'bot', 'agent', 'response',
|
||||
'markdown', 'convo', 'turn', 'approval',
|
||||
'pending', 'action', 'tool', 'content',
|
||||
'text-ide', 'block']):
|
||||
relevant.add(cls)
|
||||
|
||||
print(f"\nRelevant CSS classes ({len(relevant)}):")
|
||||
for cls in sorted(relevant)[:50]:
|
||||
print(f" .{cls}")
|
||||
|
||||
# Also search for data-testid patterns
|
||||
testid_matches = re.findall(r'data-testid\s*[:=]\s*["\']([^"\']+)["\']', content)
|
||||
if testid_matches:
|
||||
print(f"\ndata-testid values ({len(testid_matches)}):")
|
||||
for tid in sorted(set(testid_matches))[:30]:
|
||||
print(f" [{tid}]")
|
||||
|
||||
# Search for the specific bot/assistant message container patterns
|
||||
for pat in search_patterns:
|
||||
matches = re.findall(f'["\']([^"\']*{pat}[^"\']*)["\']', content, re.IGNORECASE)
|
||||
if matches:
|
||||
unique = sorted(set(matches))[:5]
|
||||
print(f"\n Pattern '{pat}': {unique}")
|
||||
|
||||
else:
|
||||
print(f"\njetskiAgent.js NOT FOUND at {main_js}")
|
||||
@@ -1,125 +0,0 @@
|
||||
/**
|
||||
* AG Native DOM Inspector — CDP를 통해 AG의 renderer에 연결하여 DOM을 덤프
|
||||
* AG가 --remote-debugging-port 없이 실행 중이므로,
|
||||
* 대안으로 AG 내부 extension의 executeJavaScript를 통해 DOM을 캡처합니다.
|
||||
*
|
||||
* 사용법: AG에서 Gravity Bridge가 활성화된 후, 이 스크립트를 extension 내에서 실행
|
||||
* 또는 AG의 DevTools Console에서 직접 실행
|
||||
*/
|
||||
|
||||
// AG DevTools Console에서 실행할 스크립트 (Ctrl+Shift+I로 열기)
|
||||
const domInspectScript = `
|
||||
(function() {
|
||||
// 1. conversation-view 찾기
|
||||
var cv = document.querySelector('[data-testid="conversation-view"]');
|
||||
console.log('=== AG Native DOM Inspector ===');
|
||||
console.log('conversation-view found:', !!cv);
|
||||
|
||||
if (!cv) {
|
||||
// document의 전체 구조를 간략히 출력
|
||||
function summarize(el, depth) {
|
||||
if (depth > 5) return '';
|
||||
var tag = el.tagName ? el.tagName.toLowerCase() : '#text';
|
||||
var cls = (el.className && typeof el.className === 'string') ? el.className.substring(0, 80) : '';
|
||||
var id = el.id || '';
|
||||
var dataAttrs = [];
|
||||
if (el.attributes) {
|
||||
for (var i = 0; i < el.attributes.length; i++) {
|
||||
if (el.attributes[i].name.startsWith('data-')) {
|
||||
dataAttrs.push(el.attributes[i].name + '=' + el.attributes[i].value.substring(0, 50));
|
||||
}
|
||||
}
|
||||
}
|
||||
var indent = ' '.repeat(depth);
|
||||
var line = indent + '<' + tag;
|
||||
if (id) line += '#' + id;
|
||||
if (cls) line += ' class="' + cls + '"';
|
||||
if (dataAttrs.length) line += ' ' + dataAttrs.join(' ');
|
||||
line += '>';
|
||||
|
||||
var result = line + '\\n';
|
||||
if (el.children && depth < 4) {
|
||||
for (var c = 0; c < Math.min(el.children.length, 15); c++) {
|
||||
result += summarize(el.children[c], depth + 1);
|
||||
}
|
||||
if (el.children.length > 15) {
|
||||
result += indent + ' ... +' + (el.children.length - 15) + ' more\\n';
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
console.log('Full body structure:');
|
||||
console.log(summarize(document.body, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. conversation-view 내부 구조 덤프
|
||||
function walkDetail(el, depth) {
|
||||
if (depth > 8) return null;
|
||||
var info = {
|
||||
tag: el.tagName ? el.tagName.toLowerCase() : '#text',
|
||||
cls: (el.className && typeof el.className === 'string') ? el.className.substring(0, 150) : '',
|
||||
dataAttrs: {},
|
||||
text: '',
|
||||
childCount: el.children ? el.children.length : 0,
|
||||
children: []
|
||||
};
|
||||
|
||||
if (el.attributes) {
|
||||
for (var i = 0; i < el.attributes.length; i++) {
|
||||
var attr = el.attributes[i];
|
||||
if (attr.name.startsWith('data-') || attr.name === 'role' || attr.name === 'aria-label' || attr.name === 'title') {
|
||||
info.dataAttrs[attr.name] = (attr.value || '').substring(0, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!el.children || el.children.length === 0) {
|
||||
var t = (el.textContent || '').trim();
|
||||
if (t.length > 0 && t.length < 150) info.text = t;
|
||||
}
|
||||
|
||||
if (el.children) {
|
||||
for (var c = 0; c < Math.min(el.children.length, 12); c++) {
|
||||
var child = walkDetail(el.children[c], depth + 1);
|
||||
if (child) info.children.push(child);
|
||||
}
|
||||
if (el.children.length > 12) {
|
||||
info.children.push({tag: '...', text: '+' + (el.children.length - 12) + ' more'});
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
var result = walkDetail(cv, 0);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
|
||||
// 3. 특정 요소들 확인
|
||||
console.log('\\n=== Key Selectors ===');
|
||||
console.log('[data-step-index] count:', cv.querySelectorAll('[data-step-index]').length);
|
||||
console.log('.text-ide-message-block-bot-color count:', cv.querySelectorAll('.text-ide-message-block-bot-color').length);
|
||||
console.log('[class*="prose"] count:', cv.querySelectorAll('[class*="prose"]').length);
|
||||
console.log('[class*="markdown"] count:', cv.querySelectorAll('[class*="markdown"]').length);
|
||||
console.log('[class*="px-2"][class*="py-1"] count:', cv.querySelectorAll('[class*="px-2"][class*="py-1"]').length);
|
||||
console.log('button count:', cv.querySelectorAll('button').length);
|
||||
|
||||
// 4. 버튼 텍스트 목록
|
||||
var btns = cv.querySelectorAll('button');
|
||||
console.log('\\n=== Buttons in conversation-view ===');
|
||||
for (var b = 0; b < btns.length; b++) {
|
||||
var txt = (btns[b].textContent || '').trim().substring(0, 80);
|
||||
console.log(' [' + b + '] "' + txt + '"');
|
||||
}
|
||||
})();
|
||||
`;
|
||||
|
||||
console.log('=== AG Native DOM Inspector Script ===');
|
||||
console.log('');
|
||||
console.log('AG DevTools Console에서 아래 스크립트를 실행하세요:');
|
||||
console.log('AG에서 DevTools를 열려면: Ctrl+Shift+I');
|
||||
console.log('');
|
||||
console.log('────────────────────────────────────────');
|
||||
console.log(domInspectScript);
|
||||
console.log('────────────────────────────────────────');
|
||||
@@ -1,90 +0,0 @@
|
||||
"""
|
||||
Inject a DOM probe into AG Native to capture the actual conversation DOM structure.
|
||||
Posts the DOM structure to the HTTP bridge's /dump-html endpoint.
|
||||
"""
|
||||
import requests, json
|
||||
|
||||
BASE = "http://127.0.0.1:34332"
|
||||
|
||||
# We already have observer script running in workbench-jetski-agent.html
|
||||
# But the issue is: the script is in the OUTER workbench, while the
|
||||
# conversation UI might be in a webview/iframe.
|
||||
|
||||
# Let's first check what the observer CAN see by triggering a dump
|
||||
# We'll post a custom HTML dump request
|
||||
|
||||
# Actually, let's analyze from a different angle:
|
||||
# The observer's scanChatBodies() uses these selectors:
|
||||
# '.text-ide-message-block-bot-color', '[data-testid*="bot"]', etc.
|
||||
# If none match, it returns nothing. But we ARE getting DOM content
|
||||
# (the garbage text), so SOMETHING is matching.
|
||||
|
||||
# The garbage text ("Running command", "content_copy", "Always run", etc.)
|
||||
# This is the tool execution UI, not the AI response.
|
||||
# Let's check what the current status says about sessionStalled
|
||||
|
||||
print("=== Bridge Status ===")
|
||||
status = requests.get(f"{BASE}/status").json()
|
||||
print(json.dumps(status, indent=2))
|
||||
|
||||
# Check if we can identify the DOM structure by examining what
|
||||
# the observer script actually matched.
|
||||
# The fact that it sends "Running command\n...\n content_copy\n Always run"
|
||||
# means it's grabbing a tool execution panel, not the AI text response.
|
||||
|
||||
# Key insight: In AG Native UI (Tailwind/React), the conversation is in
|
||||
# the SAME document as the workbench (not in a separate webview/iframe).
|
||||
# The observer IS running in the right context, BUT the CSS selectors
|
||||
# (.text-ide-message-block-bot-color) don't match AG Native's actual classes.
|
||||
|
||||
# What's happening: scanChatBodies() falls through to the broader selectors
|
||||
# like [class*="agent-convo"] or [class*="bot-message"], which might be
|
||||
# accidentally matching the tool panel UI.
|
||||
|
||||
# SOLUTION: We need to know the actual CSS class names for:
|
||||
# 1. AI response text containers (the markdown output)
|
||||
# 2. Tool call approval containers (Run/Allow/Cancel buttons)
|
||||
|
||||
print("\n=== DOM Probe via dump-html (injecting probe) ===")
|
||||
# The observer's deep-inspect didn't work, but maybe we can
|
||||
# create a targeted probe via a modified observer script
|
||||
|
||||
# Let's check what HTML files are currently being served
|
||||
print("\nChecking if we can reach the webview...")
|
||||
try:
|
||||
# The observer script's scan() function runs every 3s.
|
||||
# Let's see what it's finding by checking recent chat snapshots on disk
|
||||
import os
|
||||
bridge_path = os.path.expanduser("~/.gemini/antigravity/bridge")
|
||||
pending_dir = os.path.join(bridge_path, "pending")
|
||||
if os.path.exists(pending_dir):
|
||||
files = sorted(os.listdir(pending_dir), key=lambda f: os.path.getmtime(os.path.join(pending_dir, f)), reverse=True)
|
||||
print(f"\nRecent pending files: {len(files)}")
|
||||
for f in files[:5]:
|
||||
fpath = os.path.join(pending_dir, f)
|
||||
try:
|
||||
data = json.loads(open(fpath, encoding='utf-8').read())
|
||||
print(f" {f}: cmd=\"{data.get('command','?')[:50]}\" src={data.get('source','?')} type={data.get('step_type','?')}")
|
||||
except Exception as e:
|
||||
print(f" {f}: error: {e}")
|
||||
|
||||
# Check brain directory for session artifacts
|
||||
brain_dir = os.path.expanduser("~/.gemini/antigravity/brain/bdfc07d3-d87e-453a-b785-e38c2e9254e3")
|
||||
if os.path.exists(brain_dir):
|
||||
print(f"\nBrain dir exists: {brain_dir}")
|
||||
entries = os.listdir(brain_dir)
|
||||
print(f" Contents: {entries[:20]}")
|
||||
# Check for conversation log
|
||||
log_dir = os.path.join(brain_dir, ".system_generated", "logs")
|
||||
if os.path.exists(log_dir):
|
||||
print(f" Log dir: {os.listdir(log_dir)}")
|
||||
else:
|
||||
print(f"\nBrain dir NOT found: {brain_dir}")
|
||||
# List available brain dirs
|
||||
parent = os.path.expanduser("~/.gemini/antigravity/brain")
|
||||
if os.path.exists(parent):
|
||||
dirs = sorted(os.listdir(parent), key=lambda d: os.path.getmtime(os.path.join(parent, d)), reverse=True)
|
||||
print(f" Available: {dirs[:5]}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
@@ -1,35 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const http = require('http');
|
||||
|
||||
const logPath = 'C:\\Users\\Variet-Worker\\.gemini\\antigravity\\bridge\\extension.log';
|
||||
const log = fs.readFileSync(logPath, 'utf8');
|
||||
const match = [...log.matchAll(/port:(\d+)/g)].pop();
|
||||
if (!match) {
|
||||
console.error('No port found');
|
||||
process.exit(1);
|
||||
}
|
||||
const port = match[1];
|
||||
console.log(`Port: ${port}`);
|
||||
|
||||
const req = http.request(`http://127.0.0.1:${port}/test-rpc`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}, (res) => {
|
||||
let data = '';
|
||||
res.on('data', c => data += c);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const json = JSON.parse(data);
|
||||
const recent = json.recentTrajectories || [];
|
||||
console.log(`recentTrajectories count: ${recent.length}`);
|
||||
recent.forEach((t, i) => {
|
||||
console.log(`[${i}] googleAgentId: ${t.googleAgentId} summary: ${t.summary} ws: ${t.trajectoryMetadata?.workspaces?.[0]?.workspaceFolderAbsoluteUri}`);
|
||||
});
|
||||
} catch(e) {
|
||||
console.log(`Error parsing json: ${e.message}`);
|
||||
console.log(`Raw data: ${data.substring(0, 200)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
req.write(JSON.stringify({ method: 'GetDiagnostics', args: {} }));
|
||||
req.end();
|
||||
@@ -1,58 +0,0 @@
|
||||
const http = require('http');
|
||||
const port = 34332;
|
||||
|
||||
async function doRPC(method, args) {
|
||||
return new Promise((resolve) => {
|
||||
const req = http.request(`http://127.0.0.1:${port}/test-rpc`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}, (res) => {
|
||||
let data = '';
|
||||
res.on('data', c => data += c);
|
||||
res.on('end', () => resolve(JSON.parse(data)));
|
||||
});
|
||||
req.write(JSON.stringify({ method, args }));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let allIds = [];
|
||||
let pageToken = "";
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const args = { descending: true };
|
||||
if (pageToken) args.pageToken = pageToken;
|
||||
|
||||
console.log(`Fetching page ${i+1} with pageToken='${pageToken}'...`);
|
||||
const res = await doRPC('GetAllCascadeTrajectories', args);
|
||||
if (!res.trajectorySummaries) {
|
||||
console.log("No summaries:", res);
|
||||
break;
|
||||
}
|
||||
|
||||
const keys = Object.keys(res.trajectorySummaries);
|
||||
allIds.push(...keys);
|
||||
|
||||
console.log(` Got ${keys.length} items`);
|
||||
if (keys.length > 0) {
|
||||
console.log(` First: ${keys[0]}`);
|
||||
console.log(` Last: ${keys[keys.length-1]}`);
|
||||
}
|
||||
|
||||
if (res.nextPageToken) {
|
||||
pageToken = res.nextPageToken;
|
||||
} else {
|
||||
console.log("No nextPageToken.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Total collected: ${allIds.length}`);
|
||||
if (allIds.includes("370d1a09-1fa8-4aed-90d7-4024e36b3a2d")) {
|
||||
console.log(" FOUND 370d1a09-1fa8-4aed-90d7-4024e36b3a2d !!");
|
||||
} else {
|
||||
console.log(" Missing user active session.");
|
||||
}
|
||||
}
|
||||
main();
|
||||
@@ -1,29 +0,0 @@
|
||||
const http = require('http');
|
||||
const port = 34332;
|
||||
|
||||
function testArgs(args) {
|
||||
return new Promise((resolve) => {
|
||||
const req = http.request(`http://127.0.0.1:${port}/test-rpc`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}, (res) => {
|
||||
let data = '';
|
||||
res.on('data', c => data += c);
|
||||
res.on('end', () => {
|
||||
console.log(`Args ${JSON.stringify(args)}: ${data.substring(0, 100)}`);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
req.write(JSON.stringify({ method: 'GetCascadeTrajectorySteps', args }));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const id = "370d1a09-1fa8-4aed-90d7-4024e36b3a2d";
|
||||
await testArgs({ cascadeId: id, verbosity: 1 });
|
||||
await testArgs({ trajectoryId: id, verbosity: 1 });
|
||||
await testArgs({ id: id, verbosity: 1 });
|
||||
await testArgs({ googleAgentId: id, verbosity: 1 });
|
||||
}
|
||||
run();
|
||||
@@ -1,53 +0,0 @@
|
||||
"""Find the exact JSX structure around Allow/Deny and message-block-bot containers."""
|
||||
import re, sys, io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
bundle_path = r"C:\Users\Variet-Worker\AppData\Local\Programs\Antigravity\resources\app\out\jetskiAgent\main.js"
|
||||
content = open(bundle_path, encoding='utf-8', errors='replace').read()
|
||||
|
||||
# 1. Find the FULL Allow/Deny component (larger context)
|
||||
print("=== Allow/Deny Component (full context) ===")
|
||||
idx = content.find('label:"Allow"')
|
||||
if idx >= 0:
|
||||
start = max(0, idx - 600)
|
||||
end = min(len(content), idx + 500)
|
||||
print(content[start:end])
|
||||
print("\n" + "="*80)
|
||||
|
||||
# 2. Find text-ide-message-block-bot-color full usage
|
||||
print("\n=== text-ide-message-block-bot-color context ===")
|
||||
idx = content.find('text-ide-message-block-bot-color')
|
||||
if idx >= 0:
|
||||
start = max(0, idx - 400)
|
||||
end = min(len(content), idx + 400)
|
||||
print(content[start:end])
|
||||
print("\n" + "="*80)
|
||||
|
||||
# 3. Find data-step-index full context
|
||||
print("\n=== data-step-index context ===")
|
||||
idx = content.find('data-step-index')
|
||||
if idx >= 0:
|
||||
start = max(0, idx - 300)
|
||||
end = min(len(content), idx + 300)
|
||||
print(content[start:end])
|
||||
print("\n" + "="*80)
|
||||
|
||||
# 4. Find "Running" commands JSX pattern
|
||||
print("\n=== Running N command(s) full context ===")
|
||||
for m in re.finditer(r'Running', content[8000000:9000000], re.IGNORECASE):
|
||||
pos = 8000000 + m.start()
|
||||
ctx = content[pos-5:pos+60]
|
||||
if 'command' in ctx.lower() or 'Command' in ctx:
|
||||
start = max(0, pos - 300)
|
||||
end = min(len(content), pos + 300)
|
||||
print(f"@{pos}: {content[start:end]}")
|
||||
print("\n---\n")
|
||||
|
||||
# 5. Find the main conversation/chat container structure
|
||||
print("\n=== Conversation scroll/container patterns ===")
|
||||
for pat in ['scroll-restoration', 'data-scroll', 'overflow-y-auto.*conversation', 'chatScrollContainer']:
|
||||
for m in re.finditer(pat, content, re.IGNORECASE):
|
||||
start = max(0, m.start() - 200)
|
||||
end = min(len(content), m.end() + 200)
|
||||
print(f"Pattern '{pat}' @{m.start()}: ...{content[start:end][:400]}...")
|
||||
print()
|
||||
@@ -1,40 +0,0 @@
|
||||
"""Prepend new known-issue entry to known-issues.md"""
|
||||
import sys
|
||||
|
||||
ki_path = r"c:\Users\Variet-Worker\Desktop\gravity_control\.agents\references\known-issues.md"
|
||||
|
||||
new_entry = """### [2026-04-12] [SDK/DOM] AG Native 세션은 Cascade SDK API에 등록되지 않음 — DOM이 유일한 데이터 소스
|
||||
- **증상**: AG Native 세션에서 Discord 릴레이로 AI 응답이 전혀 전달되지 않고, 대신 UI 노이즈(`content_copy`, `Always run`, `keyboard_arrow_up`, `Cancel`)가 전송됨
|
||||
- **원인 1 (SDK)**: `GetCascadeTrajectorySteps(cascadeId=세션ID)` → `500 trajectory not found`. `GetDiagnostics` → `404`. AG Native 세션은 Cascade trajectory API에 전혀 등록되지 않는 별도 시스템
|
||||
- **원인 2 (DOM)**: `observer-script.ts` v6의 `scanChatBodies()`가 `.text-ide-message-block-bot-color` 컨테이너의 `textContent`를 통째로 가져오면서 내부 버튼/아이콘 텍스트까지 포함
|
||||
- **해결**: `observer-script.ts` v7로 전면 재설계:
|
||||
1. `[data-testid="conversation-view"]` + `[data-step-index]` 기반 step-aware 파싱
|
||||
2. `extractCleanStepText()`: 클론 후 button/svg/icon 엘리먼트 제거 → 마크다운 텍스트만 추출
|
||||
3. `extractStepContext()`: `getStepContainer()` → step 헤더 + code 블록만 추출
|
||||
4. `NOISE_RE`: Material icon 이름, 버튼 레이블, UI 텍스트 전면 차단
|
||||
5. 최초 `conversation-view` 감지 시 DOM 구조 자동 덤프 (`/dump-html`)
|
||||
- **주의**: SDK 경로(step-probe RT-CAPTURE)는 AG Native에서 사용 불가. DOM이 유일한 콘텐츠 소스이므로 AG UI 업데이트 시 `data-testid`/`data-step-index` 속성 존재 여부 반드시 확인 필요
|
||||
|
||||
"""
|
||||
|
||||
with open(ki_path, 'rb') as f:
|
||||
raw = f.read()
|
||||
try:
|
||||
content = raw.decode('utf-8')
|
||||
except:
|
||||
content = raw.decode('cp949', errors='replace')
|
||||
|
||||
# Find the "---" separator and insert after it
|
||||
marker = "---\n"
|
||||
idx = content.find(marker, content.find("archive"))
|
||||
if idx >= 0:
|
||||
insert_pos = idx + len(marker) + 1 # after ---\n\n
|
||||
# Find actual end of marker section
|
||||
after_marker = content[idx + len(marker):]
|
||||
# Insert new entry
|
||||
new_content = content[:idx + len(marker)] + "\n" + new_entry + after_marker
|
||||
with open(ki_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
print("OK: known-issue entry added")
|
||||
else:
|
||||
print("ERROR: could not find insertion point")
|
||||
@@ -1,50 +0,0 @@
|
||||
import json
|
||||
|
||||
try:
|
||||
with open(r'C:\Users\Variet-Worker\.gemini\antigravity\bridge\deep-inspect-result.json', 'r', encoding='utf-8', errors='ignore') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Print first 50 buttons from nodes
|
||||
count = 0
|
||||
print('=== First 100 Buttons in DOM Nodes ===')
|
||||
for node in data.get('nodes', []):
|
||||
label = node.get('label', 'unknown')
|
||||
btns = node.get('buttons', [])
|
||||
if btns:
|
||||
print(f'\n[Node] {label}')
|
||||
for b in btns:
|
||||
t = b.get('text', '').replace('\n', ' ').strip()
|
||||
hidden = b.get("hidden")
|
||||
cls = b.get("class")
|
||||
if t:
|
||||
print(f' - "{t[:50]}" (Hidden: {hidden}, Class: {cls[:30]})')
|
||||
count += 1
|
||||
if count > 100:
|
||||
break
|
||||
if count > 100:
|
||||
break
|
||||
|
||||
# Print first 50 buttons from webviews
|
||||
count = 0
|
||||
print('\n=== First 100 Buttons in Webviews ===')
|
||||
for probe in data.get('webviewProbes', []):
|
||||
if probe.get('success'):
|
||||
pd = probe.get('data', {})
|
||||
btns = pd.get('buttons', [])
|
||||
label = pd.get('title', 'Unknown Title') + f" (URL: {pd.get('url', 'Unknown URL')})"
|
||||
if btns:
|
||||
print(f'\n[WebviewProbe {probe.get("index")}] {label}')
|
||||
for b in btns:
|
||||
t = b.get('text', '').replace('\n', ' ').strip()
|
||||
hidden = b.get("hidden")
|
||||
cls = b.get("class")
|
||||
if t:
|
||||
print(f' - "{t[:50]}" (Hidden: {hidden}, Class: {cls[:30]})')
|
||||
count += 1
|
||||
if count > 100:
|
||||
break
|
||||
if count > 100:
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
print(f'Error reading JSON: {e}')
|
||||
@@ -1,27 +0,0 @@
|
||||
import json
|
||||
|
||||
try:
|
||||
with open(r'C:\Users\Variet-Worker\.gemini\antigravity\bridge\deep-inspect-result.json', 'r', encoding='utf-8', errors='ignore') as f:
|
||||
data = json.load(f)
|
||||
|
||||
print(f"Total Nodes: {len(data.get('nodes', []))}")
|
||||
for node in data.get('nodes', []):
|
||||
label = node.get('label', 'unknown')
|
||||
iframes = node.get('iframes', [])
|
||||
webviews = node.get('webviews', [])
|
||||
buttons = node.get('buttons', [])
|
||||
print(f"[Node] {label}")
|
||||
print(f" URL: {node.get('url', '')[:50]}")
|
||||
print(f" Total Elements: {node.get('totalElements', 0)}")
|
||||
print(f" Buttons count: {len(buttons)}")
|
||||
print(f" Iframes count: {len(iframes)}")
|
||||
print(f" Webviews count: {len(webviews)}")
|
||||
if iframes:
|
||||
for iframe in iframes:
|
||||
print(f" - iframe[{iframe.get('index')}]: accessible={iframe.get('accessible')} src={iframe.get('src', '')[:50]}")
|
||||
if webviews:
|
||||
for w in webviews:
|
||||
print(f" - webview[{w.get('index')}]: src={w.get('src', '')[:50]}")
|
||||
|
||||
except Exception as e:
|
||||
print(f'Error reading JSON: {e}')
|
||||
@@ -1,20 +0,0 @@
|
||||
import json
|
||||
|
||||
try:
|
||||
with open(r'C:\Users\Variet-Worker\.gemini\antigravity\bridge\deep-inspect-result.json', 'r', encoding='utf-8', errors='ignore') as f:
|
||||
data = json.load(f)
|
||||
|
||||
print("SEARCHING FOR CHAT TEXT IN DUMP...")
|
||||
found = False
|
||||
with open(r'C:\Users\Variet-Worker\.gemini\antigravity\bridge\deep-inspect-result.json', 'r', encoding='utf-8', errors='ignore') as f:
|
||||
raw_text = f.read()
|
||||
if '스스로 만들고 스스로' in raw_text:
|
||||
print("YES! The chat text IS in the raw JSON dump!")
|
||||
found = True
|
||||
elif 'Variet' in raw_text:
|
||||
print("Found Variet in dump.")
|
||||
else:
|
||||
print("Chat text not found in raw dump.")
|
||||
|
||||
except Exception as e:
|
||||
print(f'Error reading JSON: {e}')
|
||||
@@ -1,18 +0,0 @@
|
||||
import json
|
||||
|
||||
try:
|
||||
with open(r'C:\Users\Variet-Worker\.gemini\antigravity\bridge\deep-inspect-result.json', 'r', encoding='utf-8', errors='ignore') as f:
|
||||
data = json.load(f)
|
||||
|
||||
for node in data.get('nodes', []):
|
||||
label = node.get('label', 'unknown')
|
||||
btns = node.get('buttons', [])
|
||||
print(f"\n[Node] {label} (Total btns: {len(btns)})")
|
||||
for i, b in enumerate(btns):
|
||||
t = b.get('text', '').replace('\n', ' ').strip()
|
||||
hidden = b.get("hidden")
|
||||
cls = b.get("class")
|
||||
print(f" {i:3d}: \"{t[:50]}\" (Hidden: {hidden}, Class: {cls[:30]})")
|
||||
|
||||
except Exception as e:
|
||||
print(f'Error reading JSON: {e}')
|
||||
@@ -1,94 +0,0 @@
|
||||
/**
|
||||
* html-patcher 수정 검증 스크립트
|
||||
* 실제 workbench.html + 실제 observer-script 출력물로 패치 시뮬레이션
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 1. 실제 깨끗한 workbench.html 읽기
|
||||
const htmlPath = path.join(
|
||||
process.env.LOCALAPPDATA,
|
||||
'Programs', 'Antigravity', 'resources', 'app', 'out',
|
||||
'vs', 'code', 'electron-browser', 'workbench', 'workbench.html'
|
||||
);
|
||||
let html = fs.readFileSync(htmlPath, 'utf8');
|
||||
console.log(`[1] Clean HTML: ${html.length} chars, ${html.split('\n').length} lines`);
|
||||
console.log(` Has AG SDK: ${html.includes('AG SDK')}`);
|
||||
|
||||
// 2. 실제 observer-script.ts의 출력 시뮬레이션 (generateApprovalObserverScript)
|
||||
const observerModule = require('./extension/out/observer-script');
|
||||
const observerJS = observerModule.generateApprovalObserverScript(34332);
|
||||
console.log(`[2] Observer JS: ${observerJS.length} chars`);
|
||||
console.log(` Contains $': ${observerJS.includes("$'")}`);
|
||||
console.log(` Contains ')$': ${observerJS.includes("')$")}`);
|
||||
|
||||
// 3. 패치 시뮬레이션 — 수정 전 (BUG)
|
||||
const inlineBlock_buggy = `<!-- AG SDK INLINE [variet-gravity-bridge] -->\n<script>\n${observerJS}\n</script>\n<!-- /AG SDK INLINE [variet-gravity-bridge] -->`;
|
||||
let html_buggy = html.replace('</body>', `\n${inlineBlock_buggy}\n</body>`);
|
||||
|
||||
// 4. 패치 시뮬레이션 — 수정 후 (FIX)
|
||||
const inlineBlock = `<!-- AG SDK INLINE [variet-gravity-bridge] -->\n<script>\n${observerJS}\n</script>\n<!-- /AG SDK INLINE [variet-gravity-bridge] -->`;
|
||||
const safeInlineBlock = inlineBlock.replace(/\$/g, '$$$$');
|
||||
let html_fixed = html.replace('</body>', `\n${safeInlineBlock}\n</body>`);
|
||||
|
||||
console.log(`\n[3] BUGGY result: ${html_buggy.length} chars`);
|
||||
console.log(`[4] FIXED result: ${html_fixed.length} chars`);
|
||||
|
||||
// 5. JS 코드 추출 및 SyntaxError 검증
|
||||
function extractAndCheckJS(patchedHtml, label) {
|
||||
const match = patchedHtml.match(/<script>\n([\s\S]*?)\n<\/script>/);
|
||||
if (!match) {
|
||||
console.log(`[${label}] ERROR: <script> block not found!`);
|
||||
return false;
|
||||
}
|
||||
const jsCode = match[1];
|
||||
|
||||
// Check if original HTML structure leaked into JS
|
||||
const hasStartupComment = jsCode.includes('<!-- Startup');
|
||||
const hasWorkbenchJS = jsCode.includes('<script src="./workbench.js"');
|
||||
const hasClosingHtml = jsCode.includes('</html>') && !jsCode.includes("'</html>'");
|
||||
|
||||
console.log(`[${label}] JS code: ${jsCode.length} chars`);
|
||||
console.log(` Leaked <!-- Startup -->: ${hasStartupComment} ${hasStartupComment ? '❌ CORRUPT' : '✅ OK'}`);
|
||||
console.log(` Leaked <script src=workbench.js>: ${hasWorkbenchJS} ${hasWorkbenchJS ? '❌ CORRUPT' : '✅ OK'}`);
|
||||
console.log(` Leaked </html>: ${hasClosingHtml} ${hasClosingHtml ? '❌ CORRUPT' : '✅ OK'}`);
|
||||
|
||||
// Check NOISE_RE is intact: should contain ')$', 'i'
|
||||
const hasNoiseRE = jsCode.includes("')$', 'i'");
|
||||
console.log(` NOISE_RE ')$', 'i' preserved: ${hasNoiseRE} ${hasNoiseRE ? '✅ OK' : '❌ BROKEN'}`);
|
||||
|
||||
// Try to parse JS
|
||||
try {
|
||||
new Function(jsCode);
|
||||
console.log(` JS Syntax: ✅ VALID — no SyntaxError`);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.log(` JS Syntax: ❌ SyntaxError — ${e.message}`);
|
||||
// Find the problematic line
|
||||
const lines = jsCode.split('\n');
|
||||
const lineMatch = e.message.match(/line (\d+)/);
|
||||
if (lineMatch) {
|
||||
const lineNum = parseInt(lineMatch[1]);
|
||||
console.log(` Around line ${lineNum}:`);
|
||||
for (let i = Math.max(0, lineNum - 3); i < Math.min(lines.length, lineNum + 3); i++) {
|
||||
console.log(` ${i+1}: ${lines[i].substring(0, 100)}`);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n===== BUGGY VERSION (before fix) =====');
|
||||
const buggyOK = extractAndCheckJS(html_buggy, 'BUGGY');
|
||||
|
||||
console.log('\n===== FIXED VERSION (after fix) =====');
|
||||
const fixedOK = extractAndCheckJS(html_fixed, 'FIXED');
|
||||
|
||||
console.log('\n===== VERDICT =====');
|
||||
if (!buggyOK && fixedOK) {
|
||||
console.log('✅ FIX CONFIRMED: Buggy version has SyntaxError, fixed version is clean.');
|
||||
} else if (buggyOK && fixedOK) {
|
||||
console.log('⚠️ Both versions work — the bug may not reproduce in this environment.');
|
||||
} else if (!fixedOK) {
|
||||
console.log('❌ FIX FAILED: Fixed version still has errors!');
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
const http = require('http');
|
||||
|
||||
// Try to reload AG window via test-rpc
|
||||
const payload = JSON.stringify({
|
||||
method: "antigravity.reloadWindow",
|
||||
args: {}
|
||||
});
|
||||
|
||||
const req = http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: 34332,
|
||||
path: '/test-rpc',
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}, (res) => {
|
||||
let data = '';
|
||||
res.on('data', c => data += c);
|
||||
res.on('end', () => {
|
||||
console.log('Status:', res.statusCode);
|
||||
console.log('Response:', data);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', e => console.log('Error:', e.message));
|
||||
req.write(payload);
|
||||
req.end();
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"method": "antigravity.workbench.executeJavaScript",
|
||||
"args": {
|
||||
"code": "JSON.stringify({cv: !!document.querySelector('[data-testid=\"conversation-view\"]'), total: document.querySelectorAll('*').length, btns: document.querySelectorAll('button').length, title: document.title})"
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
const http = require('http');
|
||||
|
||||
const payload = JSON.stringify({
|
||||
method: "antigravity.workbench.executeJavaScript",
|
||||
args: {
|
||||
code: `JSON.stringify({
|
||||
title: document.title,
|
||||
total: document.querySelectorAll('*').length,
|
||||
btns: document.querySelectorAll('button').length,
|
||||
cv: !!document.querySelector('[data-testid="conversation-view"]'),
|
||||
stepEls: document.querySelectorAll('[data-step-index]').length,
|
||||
botColor: document.querySelectorAll('.text-ide-message-block-bot-color').length,
|
||||
prose: document.querySelectorAll('[class*="prose"]').length,
|
||||
agentConvo: document.querySelectorAll('[class*="agent-convo"]').length
|
||||
})`
|
||||
}
|
||||
});
|
||||
|
||||
const req = http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: 34332,
|
||||
path: '/test-rpc',
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}, (res) => {
|
||||
let data = '';
|
||||
res.on('data', c => data += c);
|
||||
res.on('end', () => {
|
||||
console.log('Status:', res.statusCode);
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
console.log('Result:', JSON.stringify(parsed, null, 2));
|
||||
} catch {
|
||||
console.log('Raw:', data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', e => console.log('Error:', e.message));
|
||||
req.write(payload);
|
||||
req.end();
|
||||
@@ -1,71 +0,0 @@
|
||||
"""Test AG SDK RPC to understand what data is available for current session."""
|
||||
import requests, json, sys, io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
BASE = "http://127.0.0.1:34332"
|
||||
SESSION = "bdfc07d3-d87e-453a-b785-e38c2e9254e3"
|
||||
|
||||
def rpc(method, args=None):
|
||||
r = requests.post(f"{BASE}/test-rpc", json={"method": method, "args": args or {}})
|
||||
if r.status_code != 200:
|
||||
print(f"❌ {method}: {r.status_code} - {r.text[:200]}")
|
||||
return None
|
||||
try:
|
||||
return r.json()
|
||||
except:
|
||||
print(f"❌ {method}: non-JSON response: {r.text[:200]}")
|
||||
return None
|
||||
|
||||
# 1. Try GetCascadeTrajectorySteps for current session
|
||||
print("=== GetCascadeTrajectorySteps ===")
|
||||
result = rpc("GetCascadeTrajectorySteps", {"cascadeId": SESSION, "verbosity": 1})
|
||||
if result and "steps" in result:
|
||||
steps = result["steps"]
|
||||
print(f" Got {len(steps)} steps")
|
||||
for i, s in enumerate(steps[-5:]):
|
||||
print(f" Step {i}: type={s.get('type','?')} status={s.get('status','?')}")
|
||||
if s.get('plannerResponse'):
|
||||
pr = s['plannerResponse']
|
||||
if isinstance(pr, str):
|
||||
print(f" plannerResponse (str): {pr[:100]}...")
|
||||
elif isinstance(pr, dict):
|
||||
print(f" plannerResponse keys: {list(pr.keys())}")
|
||||
for k, v in pr.items():
|
||||
if isinstance(v, str) and len(v) > 20 and k not in ('thinking', 'thinkingSignature'):
|
||||
print(f" {k}: {v[:100]}...")
|
||||
else:
|
||||
print(" No steps returned")
|
||||
|
||||
# 2. Try GetDiagnostics
|
||||
print("\n=== GetDiagnostics ===")
|
||||
diag = rpc("GetDiagnostics", {})
|
||||
if diag:
|
||||
if isinstance(diag, str):
|
||||
diag = json.loads(diag)
|
||||
recent = diag.get("recentTrajectories", [])
|
||||
print(f" recentTrajectories: {len(recent)}")
|
||||
for rt in recent:
|
||||
sid = rt.get("googleAgentId", "?")
|
||||
if sid.startswith("bdfc"):
|
||||
print(f" ★ Current session: {json.dumps(rt, indent=2)[:500]}")
|
||||
|
||||
# 3. Try GetAllCascadeTrajectories looking for our session
|
||||
print("\n=== GetAllCascadeTrajectories ===")
|
||||
traj = rpc("GetAllCascadeTrajectories", {"limit": 100, "descending": True})
|
||||
if traj and "trajectorySummaries" in traj:
|
||||
summaries = traj["trajectorySummaries"]
|
||||
print(f" Total trajectories: {len(summaries)}")
|
||||
for sid, data in summaries.items():
|
||||
if sid.startswith("bdfc"):
|
||||
print(f" ★ Current session keys: {list(data.keys())}")
|
||||
print(f" status: {data.get('status')}")
|
||||
print(f" stepCount: {data.get('stepCount')}")
|
||||
print(f" latestNotifyUserStep: {json.dumps(data.get('latestNotifyUserStep'), indent=2)[:300] if data.get('latestNotifyUserStep') else 'None'}")
|
||||
print(f" latestTaskBoundaryStep: {json.dumps(data.get('latestTaskBoundaryStep'), indent=2)[:300] if data.get('latestTaskBoundaryStep') else 'None'}")
|
||||
|
||||
# 4. Try other RPC methods that might exist
|
||||
print("\n=== Trying alternative RPCs ===")
|
||||
for method in ["GetCascadeStatus", "GetAgentStatus", "ListCascades", "GetCascadeInfo"]:
|
||||
result = rpc(method, {"cascadeId": SESSION})
|
||||
if result:
|
||||
print(f" {method}: {json.dumps(result)[:200]}")
|
||||
@@ -1,52 +0,0 @@
|
||||
const http = require('http');
|
||||
|
||||
// Try different RPC methods to see what's available
|
||||
const methods = [
|
||||
{ method: "antigravity.workbench.GetDiagnostics", args: {} },
|
||||
{ method: "GetDiagnostics", args: {} },
|
||||
];
|
||||
|
||||
async function tryRPC(method, args) {
|
||||
return new Promise((resolve) => {
|
||||
const payload = JSON.stringify({ method, args });
|
||||
const req = http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: 34332,
|
||||
path: '/test-rpc',
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}, (res) => {
|
||||
let data = '';
|
||||
res.on('data', c => data += c);
|
||||
res.on('end', () => {
|
||||
console.log(`\n=== ${method} (${res.statusCode}) ===`);
|
||||
if (data.length > 2000) {
|
||||
console.log(data.substring(0, 2000) + '\n... (truncated)');
|
||||
} else {
|
||||
console.log(data);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
req.on('error', e => { console.log(`${method}: ERROR ${e.message}`); resolve(); });
|
||||
req.write(payload);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
(async () => {
|
||||
for (const m of methods) {
|
||||
await tryRPC(m.method, m.args);
|
||||
}
|
||||
|
||||
// Also try the /status endpoint for full state
|
||||
const statusReq = http.get('http://127.0.0.1:34332/status', (res) => {
|
||||
let d = '';
|
||||
res.on('data', c => d += c);
|
||||
res.on('end', () => {
|
||||
console.log('\n=== /status ===');
|
||||
console.log(d);
|
||||
});
|
||||
});
|
||||
statusReq.on('error', e => console.log('Status error:', e.message));
|
||||
})();
|
||||
@@ -1,5 +0,0 @@
|
||||
Start-Sleep -Seconds 3
|
||||
$log = Get-Content 'C:\Users\Variet-Worker\.gemini\antigravity\bridge\extension.log' -Tail 500
|
||||
$port = 0
|
||||
foreach ($line in $log) { if ($line -match 'port (\d+)') { $port = $Matches[1] } }
|
||||
if ($port -gt 0) { Invoke-RestMethod -Uri "http://127.0.0.1:$port/deep-inspect"; Write-Host 'Dump success!' }
|
||||
66
test_dom.js
66
test_dom.js
@@ -1,66 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const { JSDOM } = require("jsdom");
|
||||
|
||||
try {
|
||||
const observerModule = require("./extension/out/observer-script.js");
|
||||
|
||||
const dumpRaw = fs.readFileSync('C:\\Users\\Variet-Worker\\.gemini\\antigravity\\bridge\\dump_html.json', 'utf8');
|
||||
const parseData = JSON.parse(dumpRaw);
|
||||
let htmlStr = parseData.html;
|
||||
|
||||
// Inject fake port discovery node so it passes discoverPort()
|
||||
htmlStr += `<div aria-label="Gravity Bridge Control port:1234"></div>`;
|
||||
|
||||
const dom = new JSDOM(htmlStr, { url: "http://localhost/", runScripts: "dangerously" });
|
||||
const window = dom.window;
|
||||
const document = window.document;
|
||||
|
||||
let testResults = [];
|
||||
|
||||
// Mock fetch for the observer
|
||||
window.fetch = async (url, options) => {
|
||||
if (url.includes('/ping')) {
|
||||
return { text: async () => 'pong' };
|
||||
}
|
||||
if (url.includes('/pending') && options?.method === 'POST') {
|
||||
const body = JSON.parse(options.body);
|
||||
testResults.push("✅ POST /pending intercepted! Payload:");
|
||||
testResults.push(JSON.stringify(body, null, 2));
|
||||
return { json: async () => ({ok: true, request_id: body.request_id}) };
|
||||
}
|
||||
return { json: async () => ({}) };
|
||||
};
|
||||
|
||||
// Fallback overrides
|
||||
window.console.log = (m) => testResults.push(`[Script Log] ${m}`);
|
||||
window.MutationObserver = window.MutationObserver || class { observe(){} };
|
||||
window.AbortSignal = { timeout: () => ({}) };
|
||||
|
||||
let scriptStr = observerModule.generateApprovalObserverScript(1234);
|
||||
// Brutally bypass discoverPort block and force initialization
|
||||
scriptStr = scriptStr.replace(/discoverPort\(function\(port\)\{[\s\S]*?\}\);/, "BASE='http://127.0.0.1:1234';_ready=true;startObserver();");
|
||||
scriptStr = scriptStr.replace("function scan(){", "function scan(){ log('scan() STAGE 1'); log('buttons in DOM: ' + document.querySelectorAll('button').length);");
|
||||
scriptStr = scriptStr.replace("_obs=true;", "_obs=true; log('Forcing scan'); scan();");
|
||||
|
||||
// Run script inside JSDOM
|
||||
const scriptEl = document.createElement("script");
|
||||
scriptEl.textContent = scriptStr;
|
||||
document.body.appendChild(scriptEl);
|
||||
|
||||
// Wait 3 seconds for discoverPort -> ping -> startObserver -> scheduleScan to execute
|
||||
setTimeout(() => {
|
||||
console.log("=== TEST RESULTS ===");
|
||||
console.log(testResults.join("\n"));
|
||||
if (!testResults.some(l => l.includes('POST /pending intercepted'))) {
|
||||
console.error("❌ FAILED: No POST to /pending was made. The DOM scan failed to find the dummy button or extract context.");
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log("✅ SUCCESS: The DOM extraction is functioning properly.");
|
||||
process.exit(0);
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
} catch (e) {
|
||||
console.error("Test Harness Error:", e);
|
||||
process.exit(1);
|
||||
}
|
||||
144
test_dom_mock.js
144
test_dom_mock.js
@@ -1,144 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const jsdom = require('jsdom');
|
||||
const { JSDOM } = jsdom;
|
||||
|
||||
const observerModule = require('./extension/out/observer-script.js');
|
||||
const observerCode = observerModule.generateApprovalObserverScript(8080);
|
||||
const rawDump = JSON.parse(fs.readFileSync('C:/Users/Variet-Worker/.gemini/antigravity/bridge/dump_html.json', 'utf8'));
|
||||
|
||||
// Instantiate DOM
|
||||
const dom = new JSDOM(rawDump.html, { runScripts: "dangerously", pretendToBeVisual: true, url: "http://localhost/" });
|
||||
const window = dom.window;
|
||||
const document = window.document;
|
||||
|
||||
// Polyfill offsetParent for visibility check
|
||||
Object.defineProperty(window.HTMLElement.prototype, 'offsetParent', {
|
||||
get() { return document.body; }
|
||||
});
|
||||
Object.defineProperty(window.HTMLElement.prototype, 'style', {
|
||||
get() { return { display: 'block' }; }
|
||||
});
|
||||
|
||||
// Mock innerText (JSDOM does not fully support it, but generic walker uses nodeValue / textContent)
|
||||
// Our logic uses nodeValue for TextNodes, so it will work in JSDOM out of the box!
|
||||
|
||||
// Setup the DOM tree to perfectly match a real Chat conversation
|
||||
const titleSpan = document.querySelector('span[title^="command("]');
|
||||
if(!titleSpan) {
|
||||
console.error("COULD NOT FIND command SPAN in dump?!");
|
||||
process.exit(1);
|
||||
}
|
||||
// Find the card container
|
||||
let toolContainer = titleSpan.parentElement;
|
||||
while(toolContainer && !toolContainer.className.includes("border-gray-500/10") && !toolContainer.className.includes("bg-gray-500/10")) {
|
||||
toolContainer = toolContainer.parentElement;
|
||||
}
|
||||
if(!toolContainer) toolContainer = titleSpan.parentElement; // fallback
|
||||
|
||||
// Create an AI text block just above it
|
||||
const aiChat = document.createElement('div');
|
||||
aiChat.className = 'markdown prose';
|
||||
aiChat.innerHTML = '<p>안녕하세요! 시스템을 수정하기 위해 요청하신 작업을 시작합니다. <b>디스코드 릴레이 기능 복구</b>를 위해 스크립트를 실행하겠습니다.</p>';
|
||||
|
||||
// Wrap them up in the turn container
|
||||
const parent = toolContainer.parentElement;
|
||||
const convoWrapper = document.createElement('div');
|
||||
convoWrapper.className = 'bg-agent-convo-background';
|
||||
parent.insertBefore(convoWrapper, toolContainer);
|
||||
convoWrapper.appendChild(aiChat);
|
||||
convoWrapper.appendChild(toolContainer); // Move tool inside the convo wrapper as a sibling to AI chat
|
||||
|
||||
// Add action button to the tool container
|
||||
const btn = document.createElement('button');
|
||||
btn.innerHTML = '<span class="truncate">Allow</span>';
|
||||
toolContainer.appendChild(btn);
|
||||
|
||||
console.log("Mock Button offsetParent:", btn.offsetParent ? btn.offsetParent.tagName : 'null');
|
||||
console.log("Mock Button display:", btn.style.display);
|
||||
console.log("Mock Button text:", btn.textContent);
|
||||
|
||||
// MOCK FETCH
|
||||
const fetchCalls = [];
|
||||
window.fetch = function(url, options) {
|
||||
fetchCalls.push({url, options});
|
||||
if (url.includes('/ping')) {
|
||||
return Promise.resolve({ text: function() { return Promise.resolve('pong'); } });
|
||||
}
|
||||
if (url.includes('/pending')) {
|
||||
return Promise.resolve({ json: function() { return Promise.resolve({ok: true, request_id: 'test-rid'}); } });
|
||||
}
|
||||
return Promise.resolve({ json: function() { return Promise.resolve({}); } });
|
||||
};
|
||||
|
||||
// Polyfill offsetParent for visibility check
|
||||
Object.defineProperty(window.HTMLElement.prototype, 'offsetParent', {
|
||||
get() { return document.body; }
|
||||
});
|
||||
Object.defineProperty(window.HTMLElement.prototype, 'style', {
|
||||
get() { return { display: 'block' }; }
|
||||
});
|
||||
|
||||
const originalLog = console.log;
|
||||
window.console.log = function(...args) {
|
||||
if (args.length > 0 && typeof args[0] === 'string' && args[0].includes("NOT MATCHED")) {
|
||||
let txt = args[1];
|
||||
let codes = [];
|
||||
if (txt) {
|
||||
for(let i=0; i<txt.length; i++) codes.push(txt.charCodeAt(i));
|
||||
}
|
||||
originalLog('[JSDOM-WIN]', args[0], `\nRAW="${txt}"\nCODES=[${codes.join(',')}]`);
|
||||
} else {
|
||||
originalLog('[JSDOM-WIN]', ...args);
|
||||
}
|
||||
};
|
||||
|
||||
// Inject the observer
|
||||
const scriptEl = document.createElement('script');
|
||||
scriptEl.textContent = observerCode;
|
||||
document.body.appendChild(scriptEl);
|
||||
|
||||
console.log("Observer injected, waiting for cycles...");
|
||||
console.log("Total Buttons in DOM:", document.querySelectorAll('button').length);
|
||||
|
||||
// Emulate UI mutation to trigger the MutationObserver and force an instant scan()
|
||||
setTimeout(() => {
|
||||
console.log("Triggering DOM mutation to force scan()...");
|
||||
document.body.appendChild(document.createElement('span'));
|
||||
}, 1500);
|
||||
|
||||
// Give it time to finish scan().
|
||||
setTimeout(() => {
|
||||
console.log("\n====== FETCH CALLS ======");
|
||||
if(fetchCalls.length === 0) console.log("NO FETCH CALLS MADE!");
|
||||
|
||||
fetchCalls.forEach(c => {
|
||||
console.log(`\n[${c.options ? c.options.method || 'GET' : 'GET'}] ${c.url}`);
|
||||
if(c.options && c.options.body) {
|
||||
try {
|
||||
let j = JSON.parse(c.options.body);
|
||||
console.log("[BODY] request_id:", j.request_id);
|
||||
console.log("[BODY] command:", j.command);
|
||||
console.log("[BODY] description:\n" + "=".repeat(40) + "\n" + j.description + "\n" + "=".repeat(40));
|
||||
} catch(e) {
|
||||
console.log("[BODY]", c.options.body);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Determine success
|
||||
const pendingCall = fetchCalls.find(c => c.url.includes('/pending'));
|
||||
if(pendingCall && pendingCall.options && pendingCall.options.body) {
|
||||
const payload = JSON.parse(pendingCall.options.body);
|
||||
if(payload.description.includes("안녕하세요!") && payload.description.includes("METHOD=TITLE_SPAN")) {
|
||||
console.log("\n✅ SUCCESS: Both Chat Body & Tool Command effectively extracted!");
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log("\n❌ FAIL: Payload description missing either chat text or command string!");
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.log("\n❌ FAIL: /pending never called!");
|
||||
process.exit(1);
|
||||
}
|
||||
}, 4000);
|
||||
@@ -1,98 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const { JSDOM } = require("jsdom");
|
||||
|
||||
try {
|
||||
const dumpRaw = fs.readFileSync('C:\\Users\\Variet-Worker\\.gemini\\antigravity\\bridge\\dump_html.json', 'utf8');
|
||||
const parseData = JSON.parse(dumpRaw);
|
||||
let htmlStr = parseData.html;
|
||||
|
||||
const dom = new JSDOM(htmlStr);
|
||||
const document = dom.window.document;
|
||||
|
||||
// Direct copy of functions from observer-script.ts
|
||||
function findButtonContainer(btn){
|
||||
return btn.closest('.p-1')
|
||||
|| btn.closest('.bg-agent-convo-background')
|
||||
|| btn.closest('[class*="border-gray-500/10"]')
|
||||
|| btn.closest('.monaco-list-row')
|
||||
|| btn.parentElement;
|
||||
}
|
||||
|
||||
function cleanButtonText(btn) {
|
||||
if (!btn) return '';
|
||||
var tr = btn.querySelector('.truncate');
|
||||
var txt = (tr ? tr.textContent : btn.textContent) || '';
|
||||
return txt.trim().replace(/(Alt|Ctrl|Shift|Meta)\+.*/i,'').trim();
|
||||
}
|
||||
|
||||
function extractContext(b){
|
||||
var container = findButtonContainer(b);
|
||||
if (!container) return "ERROR_NO_CONTAINER";
|
||||
|
||||
var titleSpans = container.querySelectorAll('span[title^="command("]');
|
||||
if (titleSpans && titleSpans.length > 0) {
|
||||
var t = titleSpans[0].getAttribute('title');
|
||||
if (t && t.length > 5) return "METHOD=TITLE_SPAN | " + t.substring(0, 800);
|
||||
}
|
||||
|
||||
var preEls = container.querySelectorAll('pre');
|
||||
if (preEls && preEls.length > 0) {
|
||||
var t2 = (preEls[preEls.length-1].textContent || '').trim();
|
||||
if (t2.length > 2) return "METHOD=PRE_SPAN | " + t2.substring(0, 800);
|
||||
}
|
||||
|
||||
var codeText = '';
|
||||
var codes = container.querySelectorAll('code, [class*="command"]');
|
||||
for(var i=0; i<codes.length; i++) {
|
||||
codeText += (codes[i].textContent || '').trim() + ' ';
|
||||
}
|
||||
if (codeText.length > 2) return "METHOD=CODES | " + codeText.trim().substring(0, 800);
|
||||
|
||||
var fallback = (container.textContent || '').replace(cleanButtonText(b), '').trim();
|
||||
return "METHOD=FALLBACK | " + fallback.substring(0, 500);
|
||||
}
|
||||
|
||||
// RUN TEST
|
||||
const allBtns = document.querySelectorAll('button');
|
||||
console.log(`Total buttons found: ${allBtns.length}`);
|
||||
|
||||
let tested = 0;
|
||||
for(let j=0; j<allBtns.length; j++) {
|
||||
let b = allBtns[j];
|
||||
let txt = cleanButtonText(b);
|
||||
if (txt.length <= 1) continue; // Icon
|
||||
|
||||
var PATS = [
|
||||
{ type: 'command', re: /^(?:Always\s*)?Run\b/i },
|
||||
{ type: 'permission', re: /^(?:Always\s*)?Allow\b/i },
|
||||
{ type: 'permission', re: /^(?:Always\s*)?Approve\b/i },
|
||||
{ type: 'diff_review', re: /^(?:Always\s*)?Accept\b/i }
|
||||
];
|
||||
|
||||
var matchedType=null;
|
||||
for(var p=0;p<PATS.length;p++){
|
||||
if(PATS[p].re.test(txt)){
|
||||
matchedType=PATS[p].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchedType) continue;
|
||||
|
||||
console.log(`\n✅ Matched Button: "${txt}" (Type: ${matchedType})`);
|
||||
console.log(` Extracting Context Data...`);
|
||||
console.log(` -> ` + extractContext(b));
|
||||
tested++;
|
||||
}
|
||||
|
||||
if (tested === 0) {
|
||||
console.log("❌ No actionable buttons matched!");
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log("\n✅ SUCCESS: Context fully extracted via DOM script logic.");
|
||||
process.exit(0);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
31
test_rpc.js
31
test_rpc.js
@@ -1,31 +0,0 @@
|
||||
const { LSBridge } = require('./extension/out/sdk/ls-bridge');
|
||||
|
||||
async function test() {
|
||||
const ls = new LSBridge();
|
||||
await ls.connect();
|
||||
|
||||
console.log("Testing { limit: 5, descending: true }...");
|
||||
let start = Date.now();
|
||||
const res = await ls._rpc('GetAllCascadeTrajectories', { limit: 5, descending: true });
|
||||
let duration = Date.now() - start;
|
||||
|
||||
const summaries = res.trajectorySummaries || {};
|
||||
const keys = Object.keys(summaries);
|
||||
console.log(`Execution time: ${duration}ms`);
|
||||
console.log(`Returned entries: ${keys.length}`);
|
||||
|
||||
keys.slice(0, 5).forEach((k, idx) => {
|
||||
const modT = summaries[k].lastModifiedTime || summaries[k].lastModifiedTimestamp || 'UNKNOWN';
|
||||
console.log(`[${idx}] id=${k.substring(0,8)} mod=${modT} status=${summaries[k].status}`);
|
||||
});
|
||||
|
||||
console.log("\nTesting { limit: 100, descending: true }...");
|
||||
start = Date.now();
|
||||
const res100 = await ls._rpc('GetAllCascadeTrajectories', { limit: 100, descending: true });
|
||||
duration = Date.now() - start;
|
||||
console.log(`Execution time: ${duration}ms`);
|
||||
console.log(`Returned entries: ${Object.keys(res100.trajectorySummaries || {}).length}`);
|
||||
|
||||
ls.disconnect();
|
||||
}
|
||||
test();
|
||||
@@ -1,50 +0,0 @@
|
||||
// test_ws_logic.js
|
||||
class FakeWS {
|
||||
constructor() {
|
||||
this.msgLog = [];
|
||||
this.terminated = false;
|
||||
}
|
||||
send(msg) {
|
||||
this.msgLog.push(msg);
|
||||
}
|
||||
terminate() {
|
||||
this.terminated = true;
|
||||
}
|
||||
close() {
|
||||
this.terminated = true;
|
||||
}
|
||||
}
|
||||
|
||||
// SIMULATE _startHeartbeat() logic from ws-client.ts v0.5.12
|
||||
function testLogic(isNodeWs, serverSendsPong) {
|
||||
let ws = new FakeWS();
|
||||
let connected = true;
|
||||
let lastPongTime = Date.now();
|
||||
let forceHeartbeatTimeoutIfNoPong = serverSendsPong;
|
||||
let checkCounter = 0;
|
||||
|
||||
// Fast forward 61 seconds in time
|
||||
let timeElapsed = 61000;
|
||||
let currentNow = Date.now() + timeElapsed;
|
||||
|
||||
// Simulate heartbeat timeout logic
|
||||
let conditionMet = false;
|
||||
if ((isNodeWs || forceHeartbeatTimeoutIfNoPong) && currentNow - lastPongTime > 60000) {
|
||||
conditionMet = true;
|
||||
ws.terminate();
|
||||
}
|
||||
|
||||
return {
|
||||
conditionMet: conditionMet,
|
||||
terminated: ws.terminated
|
||||
};
|
||||
}
|
||||
|
||||
console.log("Scenario 1: Node WS (native ping/pong) MUST enforce 60s timeout:");
|
||||
console.log(testLogic(true, false)); // expect true, true
|
||||
|
||||
console.log("\nScenario 2: Browser WS (fallback) + NO JSON PONG FROM SERVER MUST NOT enforce 60s timeout:");
|
||||
console.log(testLogic(false, false)); // expect false, false (PREVENTS FALSE POSITIVE)
|
||||
|
||||
console.log("\nScenario 3: Browser WS (fallback) + JSON PONG FROM SERVER MUST enforce 60s timeout:");
|
||||
console.log(testLogic(false, true)); // expect true, true (DETECTS ZOMBIE)
|
||||
Reference in New Issue
Block a user