@modootoday/datalab-extension-mcp 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +4 -4
- package/README.md +4 -4
- package/dist/{chunk-DDJ2JWP5.js → chunk-CBHNMIS3.js} +529 -322
- package/dist/cli.js +5 -5
- package/dist/{dist-Y7SH44SY.js → dist-WMNHKGZR.js} +34 -42
- package/dist/index.d.ts +28 -50
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
__export
|
|
3
3
|
} from "./chunk-MLKGABMK.js";
|
|
4
4
|
|
|
5
|
-
// ../mcp-server/dist/chunk-
|
|
5
|
+
// ../mcp-server/dist/chunk-NRS76HC5.js
|
|
6
6
|
import {
|
|
7
7
|
createServer
|
|
8
8
|
} from "http";
|
|
@@ -14391,17 +14391,14 @@ var BridgeToolDescriptorSchema = external_exports.object({
|
|
|
14391
14391
|
/** JSON Schema for the arguments, passed to the host verbatim. */
|
|
14392
14392
|
inputSchema: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
14393
14393
|
/**
|
|
14394
|
-
* MCP tool annotations, derived from the tool's tier.
|
|
14394
|
+
* MCP tool annotations, derived from the tool's tier. Optional by ABI rule.
|
|
14395
14395
|
*
|
|
14396
|
-
* Typed loosely
|
|
14397
|
-
*
|
|
14398
|
-
* every future hint a wire-contract change on a schema that binaries we
|
|
14399
|
-
* cannot reach must keep parsing.
|
|
14396
|
+
* Typed loosely because this end only forwards it; pinning the shape here
|
|
14397
|
+
* would make every future hint a wire-contract change.
|
|
14400
14398
|
*
|
|
14401
|
-
* 🔴 A server
|
|
14402
|
-
* .
|
|
14403
|
-
*
|
|
14404
|
-
* INSIDE `inputSchema`, which is forwarded verbatim.
|
|
14399
|
+
* 🔴 A server predating this field strips it, so annotations never arrive
|
|
14400
|
+
* there. Anything that must survive such a server goes inside the input
|
|
14401
|
+
* schema, which is forwarded verbatim.
|
|
14405
14402
|
*/
|
|
14406
14403
|
annotations: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
14407
14404
|
});
|
|
@@ -14414,19 +14411,15 @@ var HelloSchema = external_exports.object({
|
|
|
14414
14411
|
/** Pairing token. Not auto-attached by the browser (unlike a cookie). */
|
|
14415
14412
|
token: external_exports.string(),
|
|
14416
14413
|
/**
|
|
14417
|
-
* The extension's own id
|
|
14418
|
-
*
|
|
14419
|
-
*
|
|
14420
|
-
* newer servers verify identity from this field when the header is absent.
|
|
14421
|
-
* Optional so every binary that predates it still parses (see the ABI note).
|
|
14414
|
+
* The extension's own id. Additive: an extension service worker's privileged
|
|
14415
|
+
* loopback fetch sends no Origin header, so servers verify identity from this
|
|
14416
|
+
* field when the header is absent.
|
|
14422
14417
|
*/
|
|
14423
14418
|
extensionId: external_exports.string().optional(),
|
|
14424
14419
|
/**
|
|
14425
|
-
* Feature flags this panel build understands
|
|
14426
|
-
*
|
|
14427
|
-
*
|
|
14428
|
-
* must treat absence as "do not use". Optional with a default so every
|
|
14429
|
-
* binary that predates the field still parses.
|
|
14420
|
+
* Feature flags this panel build understands. Additive evolution happens
|
|
14421
|
+
* HERE, not via a protocol bump: a capability an old peer never sent is
|
|
14422
|
+
* simply absent, and code must treat absence as "do not use".
|
|
14430
14423
|
*/
|
|
14431
14424
|
capabilities: external_exports.array(external_exports.string()).default([])
|
|
14432
14425
|
});
|
|
@@ -14610,7 +14603,7 @@ function checkExtensionIdentity(origin, claimedExtensionId, extensionId) {
|
|
|
14610
14603
|
};
|
|
14611
14604
|
}
|
|
14612
14605
|
var READ_ONLY_TOOLS = [
|
|
14613
|
-
// --- Search-ad estimates. Uses
|
|
14606
|
+
// --- Search-ad estimates. Uses the user's own advertiser API credentials. ---
|
|
14614
14607
|
"ad_average_position_bid",
|
|
14615
14608
|
"ad_competition_density",
|
|
14616
14609
|
"ad_estimate_bid",
|
|
@@ -14665,7 +14658,7 @@ var READ_ONLY_TOOLS = [
|
|
|
14665
14658
|
"local_job_categories",
|
|
14666
14659
|
"local_region_trend",
|
|
14667
14660
|
"local_regions",
|
|
14668
|
-
// ---
|
|
14661
|
+
// --- The user's own channel analytics ---
|
|
14669
14662
|
"daily_trend",
|
|
14670
14663
|
"my_audience",
|
|
14671
14664
|
"my_blog_summary",
|
|
@@ -14694,7 +14687,7 @@ var READ_ONLY_TOOLS = [
|
|
|
14694
14687
|
"my_soaring",
|
|
14695
14688
|
"my_top_content",
|
|
14696
14689
|
"my_traffic_series",
|
|
14697
|
-
// --- Public place data, and reads of a place
|
|
14690
|
+
// --- Public place data, and reads of a place the user owns ---
|
|
14698
14691
|
"place_ai_briefing",
|
|
14699
14692
|
"place_announcements",
|
|
14700
14693
|
"place_autocomplete",
|
|
@@ -14726,8 +14719,8 @@ var READ_ONLY_TOOLS = [
|
|
|
14726
14719
|
"search_influencer",
|
|
14727
14720
|
"search_kin",
|
|
14728
14721
|
"search_news",
|
|
14729
|
-
//
|
|
14730
|
-
//
|
|
14722
|
+
// Referenced by search_blog's description, so omitting it would leave a
|
|
14723
|
+
// dangling reference for hosts.
|
|
14731
14724
|
"search_total",
|
|
14732
14725
|
"search_video",
|
|
14733
14726
|
"search_web",
|
|
@@ -14751,47 +14744,37 @@ var READ_ONLY_TOOLS = [
|
|
|
14751
14744
|
// Its siblings that suggest titles, outlines, or specs all call a language
|
|
14752
14745
|
// model, and are excluded for that reason.
|
|
14753
14746
|
"seo_scorecard",
|
|
14754
|
-
// ---
|
|
14755
|
-
// 🔴
|
|
14756
|
-
//
|
|
14757
|
-
//
|
|
14758
|
-
// 모델 호출이 0 이다 — 제외의 근거가 사라졌다.
|
|
14759
|
-
//
|
|
14760
|
-
// 읽고 제안만 하므로 여기(읽기 전용)가 맞다. 되돌릴 것이 없다.
|
|
14747
|
+
// --- Planning tools that need a model, but never call one from here ---
|
|
14748
|
+
// 🔴 The calling host already ran its own model, so calling the user's too
|
|
14749
|
+
// would bill one turn twice. These return prompt material and make no model
|
|
14750
|
+
// call, which is what lets them read and suggest here, undoing nothing.
|
|
14761
14751
|
"outline_suggest",
|
|
14762
14752
|
"seo_aeo_geo_spec",
|
|
14763
|
-
//
|
|
14764
|
-
//
|
|
14765
|
-
//
|
|
14766
|
-
// 항목만 쓰므로 blogId·sourcePostIds·sourceTitles 는 나가지 않는다.
|
|
14753
|
+
// Two-stage (analyse, then draft), so it returns the first stage's prompt and
|
|
14754
|
+
// the second only once the host hands the result back. The
|
|
14755
|
+
// style profile travels as prose inside that prompt, not as an object.
|
|
14767
14756
|
"write_draft",
|
|
14768
14757
|
"benchmark_gap",
|
|
14769
14758
|
"content_calendar",
|
|
14770
14759
|
"internal_links",
|
|
14771
14760
|
// --- Grouped with the writing tools, but a pure collector. ---
|
|
14772
|
-
//
|
|
14773
|
-
//
|
|
14774
|
-
// admitted after checking what it actually does. Classify by behaviour, not
|
|
14775
|
-
// by category.
|
|
14761
|
+
// Calls no model itself — it only gathers public data. Admitted after
|
|
14762
|
+
// checking behaviour rather than trusting the category label.
|
|
14776
14763
|
"run_research"
|
|
14777
14764
|
];
|
|
14778
14765
|
var ALLOWED = Object.freeze(new Set(READ_ONLY_TOOLS));
|
|
14779
14766
|
var EXCLUDED_TOOLS = Object.freeze({
|
|
14780
|
-
// 🔴 editor_* 는 더 이상 여기 없다 — 등급 표(TOOL_TIERS)로 옮겼다. 제외였던
|
|
14781
|
-
// 이유는 "확인을 받을 방법이 없고 되돌릴 수단도 없다" 였는데, 둘 다 생겼다:
|
|
14782
|
-
// 세션 동의 + 호출별 확인이 서고, 주입 전후로 임시저장을 부르며 그것이
|
|
14783
|
-
// 거절될 때를 위해 문서 사본을 따로 뜬다.
|
|
14784
14767
|
generate_images: "writes to your post, needs confirmation, and spends your AI credits",
|
|
14785
|
-
// 🔴 editor_read / editor_read_structure 도 더 이상 여기 없다. 보류 사유는 "탭이
|
|
14786
|
-
// 열려 있어야 하는데 외부 에이전트에겐 열 탭이 없다" 였지만, 그것은 제외할 이유가
|
|
14787
|
-
// 아니라 **읽고 나서 알게 될 사실**이다 — 탭이 없으면 도구가 그렇게 답하면 된다.
|
|
14788
|
-
// 지금은 TOOL_TIERS 에서 read 등급으로 노출된다.
|
|
14789
|
-
// Spend your own AI credits on a turn the calling host already paid for.
|
|
14790
14768
|
// Side effect outside the request/response contract.
|
|
14791
|
-
export_report: "triggers a file download"
|
|
14769
|
+
export_report: "triggers a file download",
|
|
14770
|
+
// 🔴 A host that can generate its own image has a free way onto the canvas —
|
|
14771
|
+
// gallery_image_add. Spending credits for what the caller already has is the
|
|
14772
|
+
// charge nobody meant to make.
|
|
14773
|
+
photo_ai_image_add: "spends your AI credits for an image your own app can make and hand over",
|
|
14774
|
+
video_image_realign: "spends your AI credits per photo to reach what the free scene tools already reach"
|
|
14792
14775
|
});
|
|
14793
14776
|
var TOOL_TIERS = Object.freeze({
|
|
14794
|
-
// ──
|
|
14777
|
+
// ── Photo editor (our own surface) ──────────────────────────────
|
|
14795
14778
|
photo_canvas_get: "read",
|
|
14796
14779
|
photo_gallery_list: "read",
|
|
14797
14780
|
photo_text_add: "write",
|
|
@@ -14808,13 +14791,21 @@ var TOOL_TIERS = Object.freeze({
|
|
|
14808
14791
|
photo_node_remove: "destructive",
|
|
14809
14792
|
photo_page_remove: "destructive",
|
|
14810
14793
|
photo_template_apply: "destructive",
|
|
14811
|
-
|
|
14812
|
-
//
|
|
14794
|
+
// ── Gallery: the free way in ────────────────────────────────────
|
|
14795
|
+
//
|
|
14796
|
+
// 🔴 Destructive because it reads a path off the user's disk, the same
|
|
14797
|
+
// capability editor_insert_image carries. A session toggle would open the
|
|
14798
|
+
// whole filesystem once and stay open.
|
|
14799
|
+
gallery_image_add: "destructive",
|
|
14800
|
+
// ── Video editor (our own surface) ──────────────────────────────
|
|
14813
14801
|
video_timeline_get: "read",
|
|
14814
14802
|
video_sources_list: "read",
|
|
14815
14803
|
video_scene_add: "write",
|
|
14816
14804
|
video_subtitle_set: "write",
|
|
14817
14805
|
video_scene_image_set: "write",
|
|
14806
|
+
// Brings a gallery picture into this video — an add, like video_image_add.
|
|
14807
|
+
// The disk read already happened at gallery_image_add, under its own confirm.
|
|
14808
|
+
video_image_add_from_gallery: "write",
|
|
14818
14809
|
video_scene_update: "write",
|
|
14819
14810
|
video_background_set: "write",
|
|
14820
14811
|
video_canvas_resize: "write",
|
|
@@ -14826,52 +14817,39 @@ var TOOL_TIERS = Object.freeze({
|
|
|
14826
14817
|
video_node_remove: "destructive",
|
|
14827
14818
|
video_scene_remove: "destructive",
|
|
14828
14819
|
video_narration_generate: "paid",
|
|
14829
|
-
|
|
14830
|
-
// ── 스마트에디터 읽기 ────────────────────────────────────────────
|
|
14820
|
+
// ── Post editor: reads ──────────────────────────────────────────
|
|
14831
14821
|
editor_read: "read",
|
|
14832
14822
|
editor_read_structure: "read",
|
|
14833
|
-
// ──
|
|
14823
|
+
// ── Post editor: writes ─────────────────────────────────────────
|
|
14834
14824
|
//
|
|
14835
|
-
// 🔴
|
|
14836
|
-
//
|
|
14837
|
-
//
|
|
14838
|
-
// 프로세스에 본문 쓰기를 열 수는 없었다.
|
|
14825
|
+
// 🔴 Every injection is bracketed by a vendor autosave, plus a separate copy
|
|
14826
|
+
// of the document for the cases the vendor refuses one. Without that
|
|
14827
|
+
// recovery point, editor undo would be the only way back.
|
|
14839
14828
|
//
|
|
14840
|
-
// 🔴
|
|
14841
|
-
//
|
|
14829
|
+
// 🔴 Only whole-body replacement is destructive: the others append, so the
|
|
14830
|
+
// autosave point still holds, while replacing the body overwrites it.
|
|
14842
14831
|
editor_insert_draft: "write",
|
|
14843
|
-
//
|
|
14844
|
-
//
|
|
14845
|
-
//
|
|
14832
|
+
// Opening a window edits no document, but it does create a tab in the user's
|
|
14833
|
+
// browser, and a session that has not enabled editing can do nothing with the
|
|
14834
|
+
// window anyway — one tier keeps it one thing for the user to allow.
|
|
14846
14835
|
editor_open_window: "write",
|
|
14847
|
-
// 🔴
|
|
14848
|
-
//
|
|
14849
|
-
// 없앨 수 있다.
|
|
14836
|
+
// 🔴 Closing a window cannot be undone and the user's draft may be inside, so
|
|
14837
|
+
// it is asked per call rather than covered by a forgotten session toggle.
|
|
14850
14838
|
editor_reduce_windows: "destructive",
|
|
14851
|
-
// 🔴
|
|
14852
|
-
//
|
|
14839
|
+
// 🔴 Insertion appends, so it never passes through the overwrite prompt; the
|
|
14840
|
+
// tier is the only barrier, and the user approves each image's source.
|
|
14853
14841
|
editor_insert_image: "destructive",
|
|
14854
14842
|
editor_set_title: "write",
|
|
14855
|
-
// 🔴
|
|
14856
|
-
//
|
|
14857
|
-
// `editor_set_title` 과 같은 등급이다. 이름에 표면 접두가 없는 것은 이 도구가
|
|
14858
|
-
// 사진/영상 편집기 표면에 속하지 않아서다(접두 규약은 그 둘의 이름 충돌 방지용).
|
|
14843
|
+
// 🔴 Not an editor tool, but tiered like one: picking a title candidate IS
|
|
14844
|
+
// the confirmation and overwrites the title, so it matches editor_set_title.
|
|
14859
14845
|
title_optimize: "write",
|
|
14860
14846
|
editor_undo: "write",
|
|
14861
14847
|
editor_replace: "destructive"
|
|
14862
14848
|
});
|
|
14863
14849
|
var PAID_TOOL_COSTS = Object.freeze({
|
|
14864
|
-
photo_ai_image_add: Object.freeze({
|
|
14865
|
-
basis: "AI \uC774\uBBF8\uC9C0 1\uC7A5 \uC0DD\uC131",
|
|
14866
|
-
editorAsksAgain: false
|
|
14867
|
-
}),
|
|
14868
14850
|
video_narration_generate: Object.freeze({
|
|
14869
14851
|
basis: "\uBAA9\uC18C\uB9AC\uAC00 \uC5C6\uB294 \uC7A5\uBA74 \uC218\uB9CC\uD07C \uC74C\uC131 \uC0DD\uC131",
|
|
14870
14852
|
editorAsksAgain: true
|
|
14871
|
-
}),
|
|
14872
|
-
video_image_realign: Object.freeze({
|
|
14873
|
-
basis: "\uC0AC\uC9C4 \uC218\uB9CC\uD07C \uC774\uBBF8\uC9C0 \uBD84\uC11D",
|
|
14874
|
-
editorAsksAgain: true
|
|
14875
14853
|
})
|
|
14876
14854
|
});
|
|
14877
14855
|
var HINTS_BY_TIER = Object.freeze({
|
|
@@ -14901,9 +14879,9 @@ var LOOKUP_ANNOTATIONS = Object.freeze({
|
|
|
14901
14879
|
idempotentHint: true
|
|
14902
14880
|
});
|
|
14903
14881
|
var BRIDGE_LIMITS = Object.freeze({
|
|
14904
|
-
/**
|
|
14882
|
+
/** Calls available in one burst. */
|
|
14905
14883
|
capacity: 10,
|
|
14906
|
-
/**
|
|
14884
|
+
/** Slots refilled per second. */
|
|
14907
14885
|
refillPerSecond: 1
|
|
14908
14886
|
});
|
|
14909
14887
|
var CONNECTOR_RESULT_CAP_BYTES = 4 * 1024 * 1024;
|
|
@@ -14912,7 +14890,7 @@ var RESULT_BUDGET_BYTES = Math.floor(
|
|
|
14912
14890
|
CONNECTOR_RESULT_CAP_BYTES / RESULT_HEADROOM
|
|
14913
14891
|
);
|
|
14914
14892
|
var REASON_POLICY = Object.freeze({
|
|
14915
|
-
// ---
|
|
14893
|
+
// --- Resolves on its own with time ---
|
|
14916
14894
|
rate_limited: Object.freeze({
|
|
14917
14895
|
retryable: true,
|
|
14918
14896
|
retryAfterMs: Math.ceil(1e3 / BRIDGE_LIMITS.refillPerSecond)
|
|
@@ -14920,20 +14898,20 @@ var REASON_POLICY = Object.freeze({
|
|
|
14920
14898
|
timeout: Object.freeze({ retryable: true }),
|
|
14921
14899
|
connection_lost: Object.freeze({ retryable: true }),
|
|
14922
14900
|
tool_error: Object.freeze({ retryable: true }),
|
|
14923
|
-
// ---
|
|
14901
|
+
// --- The request itself must change ---
|
|
14924
14902
|
unknown_tool: Object.freeze({ retryable: false }),
|
|
14925
14903
|
invalid_args: Object.freeze({ retryable: false }),
|
|
14926
14904
|
tool_closed: Object.freeze({ retryable: false }),
|
|
14927
14905
|
missing_in_build: Object.freeze({ retryable: false }),
|
|
14928
|
-
// --- 🔴
|
|
14906
|
+
// --- 🔴 A human must act; re-calling only nags them ---
|
|
14929
14907
|
needs_consent: Object.freeze({ retryable: false }),
|
|
14930
14908
|
not_confirmed: Object.freeze({ retryable: false }),
|
|
14931
14909
|
confirm_timeout: Object.freeze({ retryable: false }),
|
|
14932
14910
|
confirm_too_late: Object.freeze({ retryable: false }),
|
|
14933
|
-
// ---
|
|
14911
|
+
// --- The user must do something in the browser ---
|
|
14934
14912
|
multiple_editors: Object.freeze({ retryable: false }),
|
|
14935
14913
|
editor_busy: Object.freeze({ retryable: false }),
|
|
14936
|
-
// ---
|
|
14914
|
+
// --- The connection itself must be repaired ---
|
|
14937
14915
|
not_connected: Object.freeze({ retryable: false }),
|
|
14938
14916
|
unauthorized: Object.freeze({ retryable: false })
|
|
14939
14917
|
});
|
|
@@ -14962,12 +14940,9 @@ function projectTool(d) {
|
|
|
14962
14940
|
name: d.name,
|
|
14963
14941
|
description: d.description,
|
|
14964
14942
|
inputSchema: d.inputSchema,
|
|
14965
|
-
// 🔴 Re-assembly, not spread
|
|
14966
|
-
//
|
|
14967
|
-
//
|
|
14968
|
-
// dropped here in silence: the extension believes it advertised the
|
|
14969
|
-
// hints, the host never sees them, and nothing anywhere reports a
|
|
14970
|
-
// failure. Any future descriptor field must be added HERE too.
|
|
14943
|
+
// 🔴 Re-assembly, not spread: naming each field keeps a malformed peer
|
|
14944
|
+
// from smuggling extra keys into a host payload, but it also drops any
|
|
14945
|
+
// new descriptor field in silence. Add future fields HERE too.
|
|
14971
14946
|
...d.annotations ? { annotations: d.annotations } : {}
|
|
14972
14947
|
}
|
|
14973
14948
|
};
|
|
@@ -14995,6 +14970,324 @@ function projectTools(descriptors) {
|
|
|
14995
14970
|
}
|
|
14996
14971
|
return { tools, rejected };
|
|
14997
14972
|
}
|
|
14973
|
+
var BLOG_BRIEF = [
|
|
14974
|
+
"my_daily_brief",
|
|
14975
|
+
"my_blog_summary",
|
|
14976
|
+
"my_realtime",
|
|
14977
|
+
"my_traffic_series",
|
|
14978
|
+
"my_top_content",
|
|
14979
|
+
"my_soaring",
|
|
14980
|
+
"my_publish_calendar",
|
|
14981
|
+
"daily_trend",
|
|
14982
|
+
"my_moments"
|
|
14983
|
+
];
|
|
14984
|
+
var BLOG_INSIGHTS = [
|
|
14985
|
+
"my_content_audience",
|
|
14986
|
+
"my_inflow",
|
|
14987
|
+
"my_audience",
|
|
14988
|
+
"my_inflow_domain",
|
|
14989
|
+
"my_country",
|
|
14990
|
+
"my_device",
|
|
14991
|
+
"my_dwell",
|
|
14992
|
+
"my_followers",
|
|
14993
|
+
"my_revisit",
|
|
14994
|
+
"my_revenue",
|
|
14995
|
+
"my_impression_click",
|
|
14996
|
+
"my_impression_ranks",
|
|
14997
|
+
"my_channels",
|
|
14998
|
+
"my_revenue_ranks",
|
|
14999
|
+
"my_revenue_efficiency",
|
|
15000
|
+
"my_content_info",
|
|
15001
|
+
"my_content_read",
|
|
15002
|
+
"my_content_detail",
|
|
15003
|
+
"my_content_inflow"
|
|
15004
|
+
];
|
|
15005
|
+
var RESEARCH = [
|
|
15006
|
+
"local_regions",
|
|
15007
|
+
"local_job_categories",
|
|
15008
|
+
"local_region_trend",
|
|
15009
|
+
"local_area_category_rank",
|
|
15010
|
+
"content_volume",
|
|
15011
|
+
"autocomplete_keywords",
|
|
15012
|
+
"qra_keywords",
|
|
15013
|
+
"kin_question_demand",
|
|
15014
|
+
"web_read",
|
|
15015
|
+
"run_research",
|
|
15016
|
+
// Planning tools: they need a model but call none here, returning prompt
|
|
15017
|
+
// material instead. Grouped by job — all three design a post not yet written.
|
|
15018
|
+
"outline_suggest",
|
|
15019
|
+
"seo_aeo_geo_spec",
|
|
15020
|
+
"content_calendar",
|
|
15021
|
+
// The last step of that job: turn the design into an actual draft.
|
|
15022
|
+
"write_draft"
|
|
15023
|
+
];
|
|
15024
|
+
var EDITOR_CONTROL = [
|
|
15025
|
+
// 🔴 The tool that opens a tab belongs in the group that requires one: a
|
|
15026
|
+
// model choosing this group was just told it needs a tab, and the way out
|
|
15027
|
+
// has to be visible at the wall, not in some other group.
|
|
15028
|
+
"editor_open_window",
|
|
15029
|
+
"editor_reduce_windows",
|
|
15030
|
+
"editor_read",
|
|
15031
|
+
"editor_read_structure",
|
|
15032
|
+
"editor_undo",
|
|
15033
|
+
"editor_replace",
|
|
15034
|
+
"editor_insert_draft",
|
|
15035
|
+
"editor_set_title",
|
|
15036
|
+
// Both act on the open draft: title_optimize overwrites the title the moment
|
|
15037
|
+
// a candidate is picked, and internal_links reads the current draft.
|
|
15038
|
+
"title_optimize",
|
|
15039
|
+
"internal_links",
|
|
15040
|
+
"editor_insert_image",
|
|
15041
|
+
"seo_scorecard"
|
|
15042
|
+
];
|
|
15043
|
+
var BY_PREFIX = [
|
|
15044
|
+
{ slug: "serp-watch", prefixes: ["search_", "serp_"] },
|
|
15045
|
+
{ slug: "benchmark", prefixes: ["blog_", "cafe_", "influencer_"] },
|
|
15046
|
+
{ slug: "place", prefixes: ["place_"] },
|
|
15047
|
+
{ slug: "shopping", prefixes: ["shopping_"] },
|
|
15048
|
+
{ slug: "ad-bidding", prefixes: ["ad_"] },
|
|
15049
|
+
{ slug: "comment-stats", prefixes: ["comment_"] },
|
|
15050
|
+
{ slug: "photo-editor", prefixes: ["photo_"] },
|
|
15051
|
+
{ slug: "video-editor", prefixes: ["video_"] },
|
|
15052
|
+
{ slug: "keyword-research", prefixes: ["keyword_"] }
|
|
15053
|
+
];
|
|
15054
|
+
var GALLERY = ["gallery_image_add"];
|
|
15055
|
+
var EXPLICIT = [
|
|
15056
|
+
{ slug: "gallery", tools: GALLERY },
|
|
15057
|
+
{ slug: "blog-brief", tools: BLOG_BRIEF },
|
|
15058
|
+
{ slug: "blog-insights", tools: BLOG_INSIGHTS },
|
|
15059
|
+
{ slug: "keyword-research", tools: RESEARCH },
|
|
15060
|
+
{ slug: "editor-control", tools: EDITOR_CONTROL },
|
|
15061
|
+
// Named like the group but caught by none of its prefixes, so it is listed
|
|
15062
|
+
// explicitly — a similar name is not a membership rule.
|
|
15063
|
+
{ slug: "benchmark", tools: ["benchmark_gap"] }
|
|
15064
|
+
];
|
|
15065
|
+
var META = [
|
|
15066
|
+
{
|
|
15067
|
+
slug: "blog-brief",
|
|
15068
|
+
label: "\uC624\uB298\uC758 \uBE14\uB85C\uADF8 \uBE0C\uB9AC\uD551",
|
|
15069
|
+
summary: "\uB0B4 \uBE14\uB85C\uADF8\uAC00 \uC9C0\uAE08 \uC5B4\uB5A4\uC9C0 \u2014 \uBC29\uBB38\xB7\uC870\uD68C\xB7\uC778\uAE30\uAE00\xB7\uC2E4\uC2DC\uAC04"
|
|
15070
|
+
},
|
|
15071
|
+
{
|
|
15072
|
+
slug: "blog-insights",
|
|
15073
|
+
label: "\uBE14\uB85C\uADF8 \uC2EC\uCE35 \uBD84\uC11D",
|
|
15074
|
+
summary: "\uC65C \uADF8\uB7F0\uC9C0 \u2014 \uC720\uC785 \uACBD\uB85C\xB7\uC131\uBCC4\uC5F0\uB839\xB7\uCCB4\uB958\xB7\uC218\uC775\xB7\uAE00\uBCC4 \uC0C1\uC138"
|
|
15075
|
+
},
|
|
15076
|
+
{
|
|
15077
|
+
slug: "keyword-research",
|
|
15078
|
+
label: "\uD0A4\uC6CC\uB4DC\xB7\uAE00\uAC10 \uB9AC\uC11C\uCE58",
|
|
15079
|
+
summary: "\uBB34\uC5C7\uC744 \uC4F8\uC9C0 \u2014 \uD0A4\uC6CC\uB4DC \uC218\uC694\xB7\uC5F0\uAD00\uC5B4\xB7\uC790\uB3D9\uC644\uC131\xB7\uBC1C\uD589\uB7C9\xB7\uC9C0\uC2DDiN \uC9C8\uBB38\xB7\uC9C0\uC5ED \uD2B8\uB80C\uB4DC"
|
|
15080
|
+
},
|
|
15081
|
+
{
|
|
15082
|
+
slug: "serp-watch",
|
|
15083
|
+
label: "\uB124\uC774\uBC84 \uAC80\uC0C9\uACB0\uACFC \uD655\uC778",
|
|
15084
|
+
summary: "\uC774 \uAC80\uC0C9\uC5B4\uC5D0 \uC9C0\uAE08 \uB124\uC774\uBC84\uAC00 \uBB34\uC5C7\uC744 \uB744\uC6B0\uB294\uC9C0"
|
|
15085
|
+
},
|
|
15086
|
+
{
|
|
15087
|
+
slug: "benchmark",
|
|
15088
|
+
label: "\uACBD\uC7C1\uC790 \uBCA4\uCE58\uB9C8\uD0B9",
|
|
15089
|
+
summary: "\uB2E4\uB978 \uBE14\uB85C\uADF8\xB7\uCE74\uD398\xB7\uC778\uD50C\uB8E8\uC5B8\uC11C\uAC00 \uBB34\uC5C7\uC744 \uD558\uB294\uC9C0"
|
|
15090
|
+
},
|
|
15091
|
+
{
|
|
15092
|
+
slug: "place",
|
|
15093
|
+
label: "\uB124\uC774\uBC84 \uD50C\uB808\uC774\uC2A4 \uAD00\uB9AC",
|
|
15094
|
+
summary: "\uB0B4 \uB9E4\uC7A5 \uC815\uBCF4\xB7\uB9AC\uBDF0\xB7\uC608\uC57D\xB7\uCFE0\uD3F0\xB7\uB300\uAE30"
|
|
15095
|
+
},
|
|
15096
|
+
{
|
|
15097
|
+
slug: "shopping",
|
|
15098
|
+
label: "\uC1FC\uD551 \uC778\uC0AC\uC774\uD2B8",
|
|
15099
|
+
summary: "\uC774 \uC0C1\uD488\uC774 \uC5B8\uC81C \uB204\uAD6C\uC5D0\uAC8C \uD314\uB9AC\uB294\uC9C0"
|
|
15100
|
+
},
|
|
15101
|
+
{
|
|
15102
|
+
slug: "ad-bidding",
|
|
15103
|
+
label: "\uAC80\uC0C9\uAD11\uACE0 \uC785\uCC30",
|
|
15104
|
+
// The calls themselves cost nothing, hence the read tier, but they return
|
|
15105
|
+
// data only when the user's search-ad account credentials are present.
|
|
15106
|
+
summary: "\uC785\uCC30\uAC00\uC640 \uC608\uC0C1 \uC131\uACFC \u2014 \uBB34\uB8CC, \uAC80\uC0C9\uAD11\uACE0 \uACC4\uC815 \uC790\uACA9\uC99D\uBA85 \uD544\uC694"
|
|
15107
|
+
},
|
|
15108
|
+
{
|
|
15109
|
+
slug: "comment-stats",
|
|
15110
|
+
label: "\uB313\uAE00 \uBC18\uC751 \uD1B5\uACC4",
|
|
15111
|
+
summary: "\uB274\uC2A4 \uB313\uAE00\uC774 \uC5B8\uC81C \uB204\uAD6C\uC5D0\uAC8C \uB2EC\uB9AC\uB294\uC9C0"
|
|
15112
|
+
},
|
|
15113
|
+
{
|
|
15114
|
+
slug: "editor-control",
|
|
15115
|
+
label: "\uAE00\uC4F0\uAE30 \uCC3D \uC81C\uC5B4",
|
|
15116
|
+
summary: "\uC5F4\uB824 \uC788\uB294 \uB124\uC774\uBC84 \uAE00\uC4F0\uAE30 \uCC3D\uC758 \uC6D0\uACE0\uB97C \uC77D\uACE0 \uACE0\uCE5C\uB2E4",
|
|
15117
|
+
needsTab: true,
|
|
15118
|
+
openerTool: "editor_open_window",
|
|
15119
|
+
reducerTool: "editor_reduce_windows"
|
|
15120
|
+
},
|
|
15121
|
+
{
|
|
15122
|
+
slug: "photo-editor",
|
|
15123
|
+
label: "\uC0AC\uC9C4 \uD3B8\uC9D1\uAE30 \uC870\uC791",
|
|
15124
|
+
summary: "\uC0AC\uC9C4 \uD3B8\uC9D1\uAE30 \uCE94\uBC84\uC2A4\uC5D0 \uD14D\uC2A4\uD2B8\xB7\uB3C4\uD615\xB7\uC774\uBBF8\uC9C0\uB97C \uB123\uB294\uB2E4",
|
|
15125
|
+
needsTab: true,
|
|
15126
|
+
reducerTool: "editor_reduce_windows"
|
|
15127
|
+
},
|
|
15128
|
+
{
|
|
15129
|
+
slug: "video-editor",
|
|
15130
|
+
label: "\uC601\uC0C1 \uD3B8\uC9D1\uAE30 \uC870\uC791",
|
|
15131
|
+
summary: "\uC601\uC0C1 \uD3B8\uC9D1\uAE30\uC758 \uC7A5\uBA74\xB7\uC790\uB9C9\xB7\uBAA9\uC18C\uB9AC\uB97C \uB9CC\uB4E0\uB2E4",
|
|
15132
|
+
needsTab: true,
|
|
15133
|
+
reducerTool: "editor_reduce_windows"
|
|
15134
|
+
},
|
|
15135
|
+
{
|
|
15136
|
+
slug: "gallery",
|
|
15137
|
+
label: "\uC774\uBBF8\uC9C0 \uAC24\uB7EC\uB9AC",
|
|
15138
|
+
summary: "\uB0B4\uAC00 \uB9CC\uB4E0 \uC0AC\uC9C4 \uD30C\uC77C\uC744 \uAC24\uB7EC\uB9AC\uC5D0 \uB123\uC5B4 \uB450 \uD3B8\uC9D1\uAE30\uC5D0\uC11C \uC4F4\uB2E4 \u2014 \uD3B8\uC9D1\uAE30 \uCC3D\uC774 \uC5C6\uC5B4\uB3C4 \uB41C\uB2E4"
|
|
15139
|
+
}
|
|
15140
|
+
];
|
|
15141
|
+
var FALLBACK_SLUG = "other";
|
|
15142
|
+
function slugFor(name) {
|
|
15143
|
+
for (const e of EXPLICIT) {
|
|
15144
|
+
if (e.tools.includes(name)) return e.slug;
|
|
15145
|
+
}
|
|
15146
|
+
for (const p of BY_PREFIX) {
|
|
15147
|
+
if (p.prefixes.some((x) => name.startsWith(x))) return p.slug;
|
|
15148
|
+
}
|
|
15149
|
+
return FALLBACK_SLUG;
|
|
15150
|
+
}
|
|
15151
|
+
function buildToolsets(names) {
|
|
15152
|
+
const byslug = /* @__PURE__ */ new Map();
|
|
15153
|
+
for (const n of names) {
|
|
15154
|
+
const s = slugFor(n);
|
|
15155
|
+
(byslug.get(s) ?? byslug.set(s, []).get(s)).push(n);
|
|
15156
|
+
}
|
|
15157
|
+
const out = [];
|
|
15158
|
+
for (const m of META) {
|
|
15159
|
+
const tools = byslug.get(m.slug) ?? [];
|
|
15160
|
+
if (tools.length > 0) out.push({ ...m, tools });
|
|
15161
|
+
byslug.delete(m.slug);
|
|
15162
|
+
}
|
|
15163
|
+
const leftover = byslug.get(FALLBACK_SLUG) ?? [];
|
|
15164
|
+
if (leftover.length > 0) {
|
|
15165
|
+
out.push({
|
|
15166
|
+
slug: FALLBACK_SLUG,
|
|
15167
|
+
label: "\uADF8 \uBC16\uC758 \uB3C4\uAD6C",
|
|
15168
|
+
summary: "\uC544\uC9C1 \uBB36\uC74C\uC774 \uC815\uD574\uC9C0\uC9C0 \uC54A\uC740 \uB3C4\uAD6C",
|
|
15169
|
+
tools: leftover
|
|
15170
|
+
});
|
|
15171
|
+
}
|
|
15172
|
+
return out;
|
|
15173
|
+
}
|
|
15174
|
+
var CATALOG_TOOL = "datalab_catalog";
|
|
15175
|
+
var DESCRIBE_TOOL = "datalab_describe";
|
|
15176
|
+
var CALL_TOOL = "datalab_call";
|
|
15177
|
+
var CONFIRM_STATUS_TOOL = "datalab_confirm_status";
|
|
15178
|
+
var SESSION_STATE_TOOL = "datalab_session_state";
|
|
15179
|
+
function discoveryDescriptors(entries) {
|
|
15180
|
+
const sets = buildToolsets(entries.map((e) => e.name));
|
|
15181
|
+
const names = sets.map((s) => s.slug).join(", ");
|
|
15182
|
+
return [
|
|
15183
|
+
{
|
|
15184
|
+
name: CATALOG_TOOL,
|
|
15185
|
+
description: `\uB370\uC774\uD130\uB7A9\uD234\uC988\uAC00 \uD560 \uC218 \uC788\uB294 \uC77C\uC758 \uBAA9\uB85D\uC744 \uB3CC\uB824\uC900\uB2E4. \uB124\uC774\uBC84 \uBE14\uB85C\uADF8\xB7\uD0A4\uC6CC\uB4DC\xB7\uAC80\uC0C9\uACB0\uACFC\xB7\uD50C\uB808\uC774\uC2A4\xB7\uC1FC\uD551\xB7\uAD11\uACE0 \uD1B5\uACC4\uC640 \uAE00\uC4F0\uAE30/\uC0AC\uC9C4/\uC601\uC0C1 \uD3B8\uC9D1\uAE30 \uC81C\uC5B4\uAE4C\uC9C0 ${entries.length}\uAC1C \uB3C4\uAD6C\uAC00 \uC788\uACE0, \uC5EC\uAE30 \uC815\uC758\uB85C \uBCF4\uC774\uB294 \uAC83\uC740 \uADF8\uC911 \uC77C\uBD80\uB2E4. **\uC0AC\uC6A9\uC790\uAC00 \uBB34\uC5C7\uC744 \uBD80\uD0C1\uD558\uB4E0, \uC9C0\uAE08 \uBCF4\uC774\uB294 \uB3C4\uAD6C\uB85C \uC548 \uB418\uBA74 \uBA3C\uC800 \uC774\uAC83\uC744 \uBD88\uB7EC\uB77C** \u2014 \uC5C6\uB2E4\uACE0 \uB2F5\uD558\uAE30 \uC804\uC5D0 \uD655\uC778\uD55C\uB2E4. toolset \uC5C6\uC774 \uBD80\uB974\uBA74 \uBB36\uC74C \uBAA9\uB85D, \uC8FC\uBA74 \uADF8 \uBB36\uC74C\uC758 \uB3C4\uAD6C \uC774\uB984\uACFC \uC124\uBA85. \uBB36\uC74C: ${names}.`,
|
|
15186
|
+
annotations: LOOKUP_ANNOTATIONS,
|
|
15187
|
+
inputSchema: {
|
|
15188
|
+
type: "object",
|
|
15189
|
+
properties: {
|
|
15190
|
+
toolset: {
|
|
15191
|
+
type: "string",
|
|
15192
|
+
enum: sets.map((s) => s.slug),
|
|
15193
|
+
description: "\uC774 \uBB36\uC74C\uC758 \uB3C4\uAD6C \uBAA9\uB85D\uC744 \uBCF8\uB2E4. \uC0DD\uB7B5\uD558\uBA74 \uBB36\uC74C \uBAA9\uB85D\uB9CC"
|
|
15194
|
+
},
|
|
15195
|
+
query: {
|
|
15196
|
+
type: "string",
|
|
15197
|
+
description: "\uCC3E\uB294 \uB9D0(\uC608: '\uBC29\uBB38\uC790', '\uACBD\uC7C1 \uBE14\uB85C\uADF8'). \uC774\uB984\uACFC \uC124\uBA85\uC5D0\uC11C \uCC3E\uC544 \uBB36\uC74C\uC744 \uAC00\uB85C\uC9C8\uB7EC \uB3CC\uB824\uC900\uB2E4"
|
|
15198
|
+
},
|
|
15199
|
+
withSchemas: {
|
|
15200
|
+
type: "boolean",
|
|
15201
|
+
description: `\uC778\uC790 \uC2A4\uD0A4\uB9C8\uAE4C\uC9C0 \uD568\uAED8 \uBC1B\uB294\uB2E4 \u2014 \uADF8\uB7EC\uBA74 ${DESCRIBE_TOOL} \uC5C6\uC774 \uBC14\uB85C \uC2E4\uD589\uD560 \uC218 \uC788\uB2E4. toolset \uC774\uB098 query \uC640 \uAC19\uC774 \uC904 \uB54C\uB9CC \uC4F4\uB2E4`
|
|
15202
|
+
}
|
|
15203
|
+
},
|
|
15204
|
+
additionalProperties: false
|
|
15205
|
+
}
|
|
15206
|
+
},
|
|
15207
|
+
{
|
|
15208
|
+
name: DESCRIBE_TOOL,
|
|
15209
|
+
description: `\uB3C4\uAD6C\uC758 \uC778\uC790 \uC2A4\uD0A4\uB9C8\uB97C \uB3CC\uB824\uC900\uB2E4. **${CALL_TOOL} \uB85C \uBD80\uB974\uAE30 \uC804\uC5D0 \uADF8 \uB3C4\uAD6C\uC5D0 \uB300\uD574 \uCD5C\uC18C \uD55C \uBC88\uC740 \uC774\uAC83\uC744 \uD638\uCD9C\uD574\uB77C** \u2014 \uC778\uC790 \uC774\uB984\uACFC \uD544\uC218 \uC5EC\uBD80\uB97C \uC9C0\uC5B4\uB0B4\uBA74 \uAC70\uC808\uB418\uACE0, \uC655\uBCF5\uC774 \uD55C \uBC88 \uB354 \uB4E0\uB2E4. \uC774\uBBF8 \uC2A4\uD0A4\uB9C8\uB97C \uBC1B\uC544 \uB454 \uB3C4\uAD6C\uB294 \uB2E4\uC2DC \uBD80\uB97C \uD544\uC694 \uC5C6\uB2E4.`,
|
|
15210
|
+
annotations: LOOKUP_ANNOTATIONS,
|
|
15211
|
+
inputSchema: {
|
|
15212
|
+
type: "object",
|
|
15213
|
+
properties: {
|
|
15214
|
+
tools: {
|
|
15215
|
+
type: "array",
|
|
15216
|
+
items: { type: "string" },
|
|
15217
|
+
maxItems: 10,
|
|
15218
|
+
description: "\uC2A4\uD0A4\uB9C8\uB97C \uBCFC \uB3C4\uAD6C \uC774\uB984 (\uD55C \uBC88\uC5D0 10\uAC1C\uAE4C\uC9C0)"
|
|
15219
|
+
}
|
|
15220
|
+
},
|
|
15221
|
+
required: ["tools"],
|
|
15222
|
+
additionalProperties: false
|
|
15223
|
+
}
|
|
15224
|
+
},
|
|
15225
|
+
{
|
|
15226
|
+
name: CALL_TOOL,
|
|
15227
|
+
description: `${CATALOG_TOOL} \uB85C \uCC3E\uC740 \uB3C4\uAD6C\uB97C \uC2E4\uD589\uD55C\uB2E4. args \uB294 ${DESCRIBE_TOOL} \uAC00 \uC900 \uC2A4\uD0A4\uB9C8\uB97C \uADF8\uB300\uB85C \uB530\uB978\uB2E4 \u2014 \uB9DE\uC9C0 \uC54A\uC73C\uBA74 \uC2E4\uD589\uD558\uC9C0 \uC54A\uACE0 \uBB34\uC5C7\uC774 \uD2C0\uB838\uB294\uC9C0 \uB3CC\uB824\uC900\uB2E4. \uBB38\uC11C\uB97C \uACE0\uCE58\uAC70\uB098 \uC694\uAE08\uC774 \uB4DC\uB294 \uB3C4\uAD6C\uB294 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778\uC744 \uBC1B\uC740 \uB4A4\uC5D0 \uC2E4\uD589\uB41C\uB2E4.`,
|
|
15228
|
+
// 🔴 Deliberately unannotated: this tool's effect is the delegated tool's
|
|
15229
|
+
// effect, so any fixed hint would be half false. Tier is judged at run time.
|
|
15230
|
+
inputSchema: {
|
|
15231
|
+
type: "object",
|
|
15232
|
+
properties: {
|
|
15233
|
+
tool: { type: "string", description: "\uC2E4\uD589\uD560 \uB3C4\uAD6C \uC774\uB984" },
|
|
15234
|
+
args: {
|
|
15235
|
+
type: "object",
|
|
15236
|
+
description: `\uADF8 \uB3C4\uAD6C\uC758 \uC778\uC790. ${DESCRIBE_TOOL} \uC758 \uC2A4\uD0A4\uB9C8\uB97C \uB530\uB978\uB2E4`
|
|
15237
|
+
}
|
|
15238
|
+
},
|
|
15239
|
+
required: ["tool"],
|
|
15240
|
+
additionalProperties: false
|
|
15241
|
+
}
|
|
15242
|
+
},
|
|
15243
|
+
{
|
|
15244
|
+
name: CONFIRM_STATUS_TOOL,
|
|
15245
|
+
description: `\uC0AC\uC6A9\uC790 \uD655\uC778\uC744 \uAE30\uB2E4\uB9AC\uB294 \uD638\uCD9C\uC758 \uACB0\uACFC\uB97C \uAC00\uC838\uC628\uB2E4. ${CALL_TOOL} \uC774 \`awaiting_confirm\` \uACFC ticket \uC744 \uB3CC\uB824\uC92C\uC744 \uB54C\uB9CC \uC4F4\uB2E4. \uC544\uC9C1 \uB2F5\uC774 \uC5C6\uC73C\uBA74 \`pending\` \uC774\uB2C8 \uBA87 \uCD08 \uB4A4 \uB2E4\uC2DC \uD655\uC778\uD574\uB77C \u2014 **\uC6D0\uB798 \uB3C4\uAD6C\uB97C \uB2E4\uC2DC \uBD80\uB974\uC9C0 \uB9C8\uB77C**, \uD655\uC778 \uCC3D\uC774 \uD558\uB098 \uB354 \uB728\uACE0 \uC694\uAE08\uC774 \uB4DC\uB294 \uC791\uC5C5\uC774\uBA74 \uB450 \uBC88 \uCCAD\uAD6C\uB41C\uB2E4.`,
|
|
15246
|
+
annotations: LOOKUP_ANNOTATIONS,
|
|
15247
|
+
inputSchema: {
|
|
15248
|
+
type: "object",
|
|
15249
|
+
properties: {
|
|
15250
|
+
ticket: {
|
|
15251
|
+
type: "string",
|
|
15252
|
+
description: `${CALL_TOOL} \uC774 \uB3CC\uB824\uC900 ticket \uAC12 \uADF8\uB300\uB85C`
|
|
15253
|
+
}
|
|
15254
|
+
},
|
|
15255
|
+
required: ["ticket"],
|
|
15256
|
+
additionalProperties: false
|
|
15257
|
+
}
|
|
15258
|
+
}
|
|
15259
|
+
];
|
|
15260
|
+
}
|
|
15261
|
+
function sessionStateDescriptor() {
|
|
15262
|
+
return {
|
|
15263
|
+
name: SESSION_STATE_TOOL,
|
|
15264
|
+
description: "\uC9C0\uAE08 \uC774 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uD655\uC778 \uCC3D \uC5C6\uC774 \uC5BC\uB9C8\uB098 \uB354 \uC791\uC5C5\uD560 \uC218 \uC788\uB294\uC9C0 \uC54C\uB824\uC900\uB2E4. \uC5EC\uB7EC \uBC88\uC758 \uD3B8\uC9D1\uC774\uB098 \uBC30\uCE58\uB97C \uC2DC\uC791\uD558\uAE30 \uC804\uC5D0 \uBA3C\uC800 \uBD80\uB978\uB2E4 \u2014 \uB0A8\uC740 \uC2DC\uAC04\uC774 \uC9E7\uC73C\uBA74 \uB3C4\uC911\uC5D0 \uD655\uC778 \uCC3D\uC774 \uB728\uAE30 \uC2DC\uC791\uD558\uACE0, \uC0AC\uC6A9\uC790\uAC00 \uC790\uB9AC\uC5D0 \uC5C6\uC73C\uBA74 \uAC70\uAE30\uC11C \uBA48\uCD98\uB2E4.",
|
|
15265
|
+
inputSchema: {
|
|
15266
|
+
type: "object",
|
|
15267
|
+
properties: {},
|
|
15268
|
+
additionalProperties: false
|
|
15269
|
+
},
|
|
15270
|
+
annotations: LOOKUP_ANNOTATIONS
|
|
15271
|
+
};
|
|
15272
|
+
}
|
|
15273
|
+
function allowlistNames() {
|
|
15274
|
+
return [.../* @__PURE__ */ new Set([...READ_ONLY_TOOLS, ...Object.keys(TOOL_TIERS)])];
|
|
15275
|
+
}
|
|
15276
|
+
function staticDiscoveryCatalog() {
|
|
15277
|
+
const entries = allowlistNames().map((name) => ({
|
|
15278
|
+
name,
|
|
15279
|
+
description: "",
|
|
15280
|
+
inputSchema: {}
|
|
15281
|
+
}));
|
|
15282
|
+
return [...discoveryDescriptors(entries), sessionStateDescriptor()].map(
|
|
15283
|
+
(e) => ({
|
|
15284
|
+
name: e.name,
|
|
15285
|
+
description: e.description,
|
|
15286
|
+
inputSchema: e.inputSchema,
|
|
15287
|
+
...e.annotations ? { annotations: { ...e.annotations } } : {}
|
|
15288
|
+
})
|
|
15289
|
+
);
|
|
15290
|
+
}
|
|
14998
15291
|
var BRIDGE_USER_MESSAGES = Object.freeze({
|
|
14999
15292
|
/** Tool call arrived while no panel is connected. */
|
|
15000
15293
|
panelClosed: "\uD06C\uB86C\uC5D0\uC11C \uB370\uC774\uD130\uB7A9\uD234\uC988 \uD328\uB110\uC774 \uB2EB\uD600 \uC788\uC5B4\uC694. \uD06C\uB86C\uC744 \uC5F4\uACE0 \uB370\uC774\uD130\uB7A9\uD234\uC988 \uD328\uB110\uC744 \uC5F4\uC5B4 \uB450\uBA74 \uBC14\uB85C \uB3D9\uC791\uD574\uC694.",
|
|
@@ -15003,12 +15296,9 @@ var BRIDGE_USER_MESSAGES = Object.freeze({
|
|
|
15003
15296
|
/** The session hit the per-session rate limit. */
|
|
15004
15297
|
rateLimited: "\uC694\uCCAD\uC774 \uB108\uBB34 \uC7A6\uC544\uC694. \uC7A0\uAE50 \uAE30\uB2E4\uB838\uB2E4\uAC00 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
|
|
15005
15298
|
/**
|
|
15006
|
-
* The local connector accepted nothing back inside its ceiling.
|
|
15007
|
-
*
|
|
15008
|
-
*
|
|
15009
|
-
* which does not help when the browser side is fine and the connector is the
|
|
15010
|
-
* part that stopped. Pointing at the wrong thing costs them a round of
|
|
15011
|
-
* checking before they can even start fixing it.
|
|
15299
|
+
* The local connector accepted nothing back inside its ceiling. Distinct
|
|
15300
|
+
* from panelClosed: that one sends the user to Chrome, which does not help
|
|
15301
|
+
* when the browser side is fine and the connector is what stopped.
|
|
15012
15302
|
*/
|
|
15013
15303
|
connectorStuck: "\uC5F0\uACB0 \uD504\uB85C\uADF8\uB7A8\uC774 \uC751\uB2F5\uD558\uC9C0 \uC54A\uC544\uC694. \uC7A0\uC2DC \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uACE0, \uACC4\uC18D \uADF8\uB7EC\uBA74 \uD06C\uB86C \uD328\uB110\uC5D0\uC11C MCP \uC5F0\uACB0\uC744 \uAED0\uB2E4\uAC00 \uB2E4\uC2DC \uCF1C \uC8FC\uC138\uC694."
|
|
15014
15304
|
});
|
|
@@ -15053,22 +15343,10 @@ var PendingRegistry = class {
|
|
|
15053
15343
|
/**
|
|
15054
15344
|
* The id a request is known by, unique for the life of this registry.
|
|
15055
15345
|
*
|
|
15056
|
-
* 🔴 The prefix is
|
|
15057
|
-
*
|
|
15058
|
-
*
|
|
15059
|
-
*
|
|
15060
|
-
* server it was talking to goes away, reconnect to the replacement, and then
|
|
15061
|
-
* post its answer for the request it was given. With a bare counter that id
|
|
15062
|
-
* names a live request in the new server — someone else's — and the answer
|
|
15063
|
-
* settles the wrong call. Nothing detects it, because from the inside it is
|
|
15064
|
-
* an ordinary reply arriving for an id that exists.
|
|
15065
|
-
*
|
|
15066
|
-
* Making ids not collide is enough on its own: an answer for an id nobody is
|
|
15067
|
-
* waiting on is already dropped in silence, which is exactly the right
|
|
15068
|
-
* outcome for a reply that arrives after its server is gone.
|
|
15069
|
-
*
|
|
15070
|
-
* The value stays an opaque string that the panel echoes back untouched, so
|
|
15071
|
-
* nothing else has to know this changed.
|
|
15346
|
+
* 🔴 The prefix is load-bearing. This process is replaced routinely while
|
|
15347
|
+
* the panel outlives it, so a panel can reconnect to a replacement and post
|
|
15348
|
+
* an answer there. A bare counter restarts at one, so that id would settle
|
|
15349
|
+
* someone else's live call, indistinguishable from an ordinary reply.
|
|
15072
15350
|
*/
|
|
15073
15351
|
nextId() {
|
|
15074
15352
|
this.#seq += 1;
|
|
@@ -15079,10 +15357,9 @@ var PendingRegistry = class {
|
|
|
15079
15357
|
return this.#entries.size;
|
|
15080
15358
|
}
|
|
15081
15359
|
/**
|
|
15082
|
-
* Register a request and get the promise the host will await.
|
|
15083
|
-
*
|
|
15084
|
-
*
|
|
15085
|
-
* races back before `send` returns still finds its entry.
|
|
15360
|
+
* Register a request and get the promise the host will await. Created before
|
|
15361
|
+
* the caller sends anything, so a reply that races back before the send
|
|
15362
|
+
* returns still finds its entry.
|
|
15086
15363
|
*/
|
|
15087
15364
|
register(id) {
|
|
15088
15365
|
return new Promise((resolve) => {
|
|
@@ -15093,13 +15370,9 @@ var PendingRegistry = class {
|
|
|
15093
15370
|
});
|
|
15094
15371
|
}
|
|
15095
15372
|
/**
|
|
15096
|
-
* Settle a request by id.
|
|
15097
|
-
*
|
|
15098
|
-
*
|
|
15099
|
-
* the request out, or the extension reconnected and replayed. Neither is worth
|
|
15100
|
-
* crashing a server over, and both are expected under a flaky stream.
|
|
15101
|
-
*
|
|
15102
|
-
* @returns whether an entry was actually settled.
|
|
15373
|
+
* Settle a request by id. A reply for an unknown id is dropped rather than
|
|
15374
|
+
* thrown — the request already timed out, or the extension reconnected and
|
|
15375
|
+
* replayed, and both are expected under a flaky stream.
|
|
15103
15376
|
*/
|
|
15104
15377
|
settle(id, outcome) {
|
|
15105
15378
|
const entry = this.#entries.get(id);
|
|
@@ -15108,11 +15381,7 @@ var PendingRegistry = class {
|
|
|
15108
15381
|
entry.settle(outcome);
|
|
15109
15382
|
return true;
|
|
15110
15383
|
}
|
|
15111
|
-
/**
|
|
15112
|
-
* Fail everything past its deadline. Call on a tick.
|
|
15113
|
-
*
|
|
15114
|
-
* @returns how many were expired.
|
|
15115
|
-
*/
|
|
15384
|
+
/** Fail everything past its deadline. Called on a tick by the daemon. */
|
|
15116
15385
|
sweep() {
|
|
15117
15386
|
const now = this.#now();
|
|
15118
15387
|
let expired = 0;
|
|
@@ -15131,10 +15400,8 @@ var PendingRegistry = class {
|
|
|
15131
15400
|
}
|
|
15132
15401
|
/**
|
|
15133
15402
|
* Fail everything now — the stream dropped, so nothing in flight can land.
|
|
15134
|
-
*
|
|
15135
|
-
*
|
|
15136
|
-
* the panel closes would hang until its own deadline, and the host would sit
|
|
15137
|
-
* there for 30s per call with no explanation.
|
|
15403
|
+
* Without this, every request outstanding when the panel closes would hang
|
|
15404
|
+
* until its own deadline with no explanation.
|
|
15138
15405
|
*/
|
|
15139
15406
|
rejectAll(reason, message) {
|
|
15140
15407
|
const n = this.#entries.size;
|
|
@@ -15149,9 +15416,9 @@ var DEFAULT_RATE_LIMIT = BRIDGE_LIMITS;
|
|
|
15149
15416
|
var STALE_TOKEN_LIMIT = 5;
|
|
15150
15417
|
var Bridge = class {
|
|
15151
15418
|
#deps;
|
|
15152
|
-
// 🔴
|
|
15153
|
-
//
|
|
15154
|
-
//
|
|
15419
|
+
// 🔴 Built in the constructor, not at field initialisation: initialisers run
|
|
15420
|
+
// before the constructor body, so deps would not exist yet and the injected
|
|
15421
|
+
// clock and timeout would silently fall back to their defaults.
|
|
15155
15422
|
#pending;
|
|
15156
15423
|
#session = null;
|
|
15157
15424
|
#sessionSeq = 0;
|
|
@@ -15159,12 +15426,9 @@ var Bridge = class {
|
|
|
15159
15426
|
#badTokenStreak = 0;
|
|
15160
15427
|
#bucketTokens = 0;
|
|
15161
15428
|
#bucketRefilledAt = 0;
|
|
15162
|
-
// Last catalog the panel reported, kept across disconnects
|
|
15163
|
-
//
|
|
15164
|
-
//
|
|
15165
|
-
// that cache empty forever. Serving the last-known list keeps the catalog
|
|
15166
|
-
// stable across a panel flap — honest because a call while disconnected still
|
|
15167
|
-
// fails through the isError channel (never executes without a live panel).
|
|
15429
|
+
// Last catalog the panel reported, kept across disconnects: many hosts cache
|
|
15430
|
+
// the first list they get, so serving an empty one during a brief outage
|
|
15431
|
+
// would empty that cache for good. A call while disconnected still fails.
|
|
15168
15432
|
#cachedTools = [];
|
|
15169
15433
|
constructor(deps) {
|
|
15170
15434
|
this.#deps = deps;
|
|
@@ -15181,56 +15445,29 @@ var Bridge = class {
|
|
|
15181
15445
|
return this.#session !== null;
|
|
15182
15446
|
}
|
|
15183
15447
|
/**
|
|
15184
|
-
* The last catalog the panel reported,
|
|
15185
|
-
* this daemon's life. Served
|
|
15186
|
-
*
|
|
15448
|
+
* The last catalog the panel reported, empty if it has never connected in
|
|
15449
|
+
* this daemon's life. Served while the panel is disconnected so a host does
|
|
15450
|
+
* not cache an empty list it never re-fetches.
|
|
15187
15451
|
*/
|
|
15188
15452
|
get lastKnownTools() {
|
|
15189
15453
|
return this.#cachedTools;
|
|
15190
15454
|
}
|
|
15191
15455
|
/**
|
|
15192
|
-
* Does
|
|
15193
|
-
*
|
|
15194
|
-
*
|
|
15456
|
+
* Does the presented value match the pairing token? Authorises the shutdown
|
|
15457
|
+
* an updating adapter sends, which no other local process should be able to
|
|
15458
|
+
* trigger — hence the token.
|
|
15195
15459
|
*/
|
|
15196
15460
|
verifyToken(presented) {
|
|
15197
15461
|
return checkToken(presented ?? void 0, this.#deps.token).ok;
|
|
15198
15462
|
}
|
|
15199
|
-
/**
|
|
15200
|
-
* Run the handshake.
|
|
15201
|
-
*
|
|
15202
|
-
* Order is deliberate: origin, then token, then version. Origin and token are
|
|
15203
|
-
* the security gates and must both pass before we tell an unauthenticated public-copy-exception: 우리 게이트를 긍정 서술
|
|
15204
|
-
* caller anything — including which protocol versions we speak, which is a
|
|
15205
|
-
* small fingerprint we owe nobody.
|
|
15206
|
-
*/
|
|
15207
15463
|
/**
|
|
15208
15464
|
* The extension is right about the token, and we can be wrong about it.
|
|
15209
15465
|
*
|
|
15210
|
-
* 🔴 This server is
|
|
15211
|
-
*
|
|
15212
|
-
* issues a fresh
|
|
15213
|
-
*
|
|
15214
|
-
*
|
|
15215
|
-
* help — the new adapter finds a same-version server on the port and leaves it
|
|
15216
|
-
* alone. There is no way out from the outside either: stepping aside is gated
|
|
15217
|
-
* on the very token that no longer matches.
|
|
15218
|
-
*
|
|
15219
|
-
* So the server has to notice on its own. A run of refusals from the right
|
|
15220
|
-
* extension, with nobody connected, says one thing — the token here is stale.
|
|
15221
|
-
* Exiting costs nothing: the next call starts a fresh one that reads the
|
|
15222
|
-
* current token.
|
|
15223
|
-
*
|
|
15224
|
-
* Both conditions matter. The identity gate ahead of this drops anything not
|
|
15225
|
-
* claiming to be our extension before the token is looked at, and "nobody
|
|
15226
|
-
* connected" keeps stray refusals from ending a server that is working for
|
|
15227
|
-
* someone right now.
|
|
15228
|
-
*
|
|
15229
|
-
* The identity gate trusts the Origin header when one is present — a browser
|
|
15230
|
-
* sets it, a native process can forge it. A local program can therefore drive
|
|
15231
|
-
* this to an exit, and gains nothing: running as the user, it could kill the
|
|
15232
|
-
* process outright. What this filters is stray traffic, not an intruder who is
|
|
15233
|
-
* already past the door.
|
|
15466
|
+
* 🔴 This server is detached and outlives the app that started it, and a
|
|
15467
|
+
* replacement only takes over when its version is newer. Once the user
|
|
15468
|
+
* issues a fresh token, stepping aside is gated on the very token that no
|
|
15469
|
+
* longer matches, so the server has to notice on its own. A run of refusals
|
|
15470
|
+
* with nobody connected says exactly that, and exiting costs nothing.
|
|
15234
15471
|
*/
|
|
15235
15472
|
#noteStaleToken() {
|
|
15236
15473
|
if (this.#session) return;
|
|
@@ -15242,6 +15479,13 @@ var Bridge = class {
|
|
|
15242
15479
|
this.#badTokenStreak = 0;
|
|
15243
15480
|
this.#deps.onStaleToken?.();
|
|
15244
15481
|
}
|
|
15482
|
+
/**
|
|
15483
|
+
* Run the handshake.
|
|
15484
|
+
*
|
|
15485
|
+
* 🔴 Order is deliberate: identity, then token, then version. Both gates must
|
|
15486
|
+
* pass before an unauthorised caller learns anything — including which
|
|
15487
|
+
* protocol versions we speak, which is a fingerprint we owe nobody.
|
|
15488
|
+
*/
|
|
15245
15489
|
handshake(raw, origin) {
|
|
15246
15490
|
const parsed = HelloSchema.safeParse(raw);
|
|
15247
15491
|
if (!parsed.success) {
|
|
@@ -15289,9 +15533,9 @@ var Bridge = class {
|
|
|
15289
15533
|
this.#pending.rejectAll("superseded", "The side panel reconnected.");
|
|
15290
15534
|
}
|
|
15291
15535
|
this.#session = {
|
|
15292
|
-
//
|
|
15293
|
-
//
|
|
15294
|
-
//
|
|
15536
|
+
// 🔴 A counter, not a timestamp: a panel reload produces two handshakes
|
|
15537
|
+
// inside one millisecond, and the new session must never be
|
|
15538
|
+
// indistinguishable from the one it replaced.
|
|
15295
15539
|
sessionId: `s${(this.#sessionSeq += 1).toString(36)}`,
|
|
15296
15540
|
protocolVersion: negotiated.version,
|
|
15297
15541
|
extensionVersion: parsed.data.extensionVersion
|
|
@@ -15304,8 +15548,8 @@ var Bridge = class {
|
|
|
15304
15548
|
protocolVersion: negotiated.version,
|
|
15305
15549
|
serverVersion: this.#deps.serverVersion,
|
|
15306
15550
|
sessionId: this.#session.sessionId,
|
|
15307
|
-
//
|
|
15308
|
-
//
|
|
15551
|
+
// No optional behaviours in this build. The field exists so additive
|
|
15552
|
+
// evolution has somewhere to go that is not a protocol bump.
|
|
15309
15553
|
capabilities: []
|
|
15310
15554
|
};
|
|
15311
15555
|
}
|
|
@@ -15356,14 +15600,9 @@ var Bridge = class {
|
|
|
15356
15600
|
/**
|
|
15357
15601
|
* Ask the panel for its tools, and project them.
|
|
15358
15602
|
*
|
|
15359
|
-
* Projection is protocol conformance only — name grammar, descriptor
|
|
15360
|
-
* catalog size
|
|
15361
|
-
*
|
|
15362
|
-
* compromised panel"; that justification was false — anything able to
|
|
15363
|
-
* corrupt the panel's catalog already holds the browser session itself and
|
|
15364
|
-
* needs no tool names from us — and the copy's real effect was to silently
|
|
15365
|
-
* hide every tool added after this binary shipped. Membership is enforced
|
|
15366
|
-
* where the session and the execution actually live: the panel executor.
|
|
15603
|
+
* 🔴 Projection is protocol conformance only — name grammar, descriptor
|
|
15604
|
+
* shape, catalog size — with no membership check. Membership is enforced
|
|
15605
|
+
* where the session and the execution live, in the panel's own executor.
|
|
15367
15606
|
*/
|
|
15368
15607
|
async listTools() {
|
|
15369
15608
|
const out = await this.#request({
|
|
@@ -15391,10 +15630,9 @@ var Bridge = class {
|
|
|
15391
15630
|
return out.result;
|
|
15392
15631
|
}
|
|
15393
15632
|
/**
|
|
15394
|
-
* Refill-then-take on the session's token bucket.
|
|
15395
|
-
*
|
|
15396
|
-
*
|
|
15397
|
-
* injectable `now` keeps it testable without waiting real seconds.
|
|
15633
|
+
* Refill-then-take on the session's token bucket. The refill is computed
|
|
15634
|
+
* lazily on each take, so there is no timer to leak and an injected clock
|
|
15635
|
+
* keeps it testable without waiting real seconds.
|
|
15398
15636
|
*/
|
|
15399
15637
|
#takeRateToken() {
|
|
15400
15638
|
const limit = this.#deps.rateLimit ?? DEFAULT_RATE_LIMIT;
|
|
@@ -15496,9 +15734,9 @@ function frameMcpResponse(payload, accept) {
|
|
|
15496
15734
|
body: json32
|
|
15497
15735
|
};
|
|
15498
15736
|
}
|
|
15499
|
-
function
|
|
15737
|
+
function coldCatalog(id, accept) {
|
|
15500
15738
|
return frameMcpResponse(
|
|
15501
|
-
|
|
15739
|
+
rpcResult(id, { tools: staticDiscoveryCatalog() }),
|
|
15502
15740
|
accept
|
|
15503
15741
|
);
|
|
15504
15742
|
}
|
|
@@ -15514,8 +15752,7 @@ async function handleMcpRequest(bridge, body, accept) {
|
|
|
15514
15752
|
const method = req.method;
|
|
15515
15753
|
if (method === "tools/list") {
|
|
15516
15754
|
if (!bridge.connected) {
|
|
15517
|
-
if (bridge.lastKnownTools.length === 0)
|
|
15518
|
-
return coldCatalogError(id, accept);
|
|
15755
|
+
if (bridge.lastKnownTools.length === 0) return coldCatalog(id, accept);
|
|
15519
15756
|
return frameMcpResponse(
|
|
15520
15757
|
rpcResult(id, { tools: bridge.lastKnownTools }),
|
|
15521
15758
|
accept
|
|
@@ -15526,8 +15763,7 @@ async function handleMcpRequest(bridge, body, accept) {
|
|
|
15526
15763
|
return frameMcpResponse(rpcResult(id, { tools }), accept);
|
|
15527
15764
|
} catch (err) {
|
|
15528
15765
|
if (err instanceof BridgeError) {
|
|
15529
|
-
if (bridge.lastKnownTools.length === 0)
|
|
15530
|
-
return coldCatalogError(id, accept);
|
|
15766
|
+
if (bridge.lastKnownTools.length === 0) return coldCatalog(id, accept);
|
|
15531
15767
|
return frameMcpResponse(
|
|
15532
15768
|
rpcResult(id, { tools: bridge.lastKnownTools }),
|
|
15533
15769
|
accept
|
|
@@ -15741,8 +15977,8 @@ function createHttpBridge(opts) {
|
|
|
15741
15977
|
lifecycle?.bump?.();
|
|
15742
15978
|
void readJson(req, MAX_MCP_BODY_BYTES2).then((body) => handleMcpRequest(opts.bridge, body, req.headers.accept)).catch(
|
|
15743
15979
|
() => (
|
|
15744
|
-
//
|
|
15745
|
-
// dropped socket;
|
|
15980
|
+
// An unparseable or oversized body still owes a JSON-RPC answer, not
|
|
15981
|
+
// a dropped socket; a null body routes through as method-not-found.
|
|
15746
15982
|
handleMcpRequest(opts.bridge, null, req.headers.accept)
|
|
15747
15983
|
)
|
|
15748
15984
|
).then((out) => {
|
|
@@ -15786,7 +16022,8 @@ function createHttpBridge(opts) {
|
|
|
15786
16022
|
"content-type": "text/event-stream",
|
|
15787
16023
|
"cache-control": "no-cache",
|
|
15788
16024
|
connection: "keep-alive",
|
|
15789
|
-
//
|
|
16025
|
+
// Buffering would hold small frames back, and a bridge frame is small
|
|
16026
|
+
// by nature.
|
|
15790
16027
|
"x-accel-buffering": "no"
|
|
15791
16028
|
});
|
|
15792
16029
|
sse = res;
|
|
@@ -15831,7 +16068,8 @@ function createHttpBridge(opts) {
|
|
|
15831
16068
|
res.writeHead(200, {
|
|
15832
16069
|
"content-type": out.contentType,
|
|
15833
16070
|
"content-length": String(out.byteLength),
|
|
15834
|
-
//
|
|
16071
|
+
// The extension shows this in its confirmation, so the user knows
|
|
16072
|
+
// which file is about to be used.
|
|
15835
16073
|
"x-file-name": encodeURIComponent(out.fileName),
|
|
15836
16074
|
"cache-control": "no-store"
|
|
15837
16075
|
});
|
|
@@ -16088,10 +16326,8 @@ var Lifecycle = class {
|
|
|
16088
16326
|
return this.#refs;
|
|
16089
16327
|
}
|
|
16090
16328
|
/**
|
|
16091
|
-
* Begin idle accounting.
|
|
16092
|
-
*
|
|
16093
|
-
* Called once at startup so a daemon nobody ever uses still reclaims itself:
|
|
16094
|
-
* refs are zero, so the window arms immediately.
|
|
16329
|
+
* Begin idle accounting. Called once at startup so a daemon nobody ever uses
|
|
16330
|
+
* still reclaims itself.
|
|
16095
16331
|
*/
|
|
16096
16332
|
start() {
|
|
16097
16333
|
if (this.#refs === 0) this.#arm();
|
|
@@ -16107,12 +16343,9 @@ var Lifecycle = class {
|
|
|
16107
16343
|
if (this.#refs === 0) this.#arm();
|
|
16108
16344
|
}
|
|
16109
16345
|
/**
|
|
16110
|
-
* Traffic crossed the daemon — reset the debounce.
|
|
16111
|
-
*
|
|
16112
|
-
*
|
|
16113
|
-
* With refs at zero (a stateless `/mcp` POST between adapter connections) it
|
|
16114
|
-
* re-arms the full window, so a steady drip of requests keeps the daemon warm
|
|
16115
|
-
* without ever holding a long-lived ref.
|
|
16346
|
+
* Traffic crossed the daemon — reset the debounce. A no-op while refs are
|
|
16347
|
+
* held; at zero refs it re-arms the window, so a steady drip of stateless
|
|
16348
|
+
* requests keeps the daemon warm without holding a long-lived ref.
|
|
16116
16349
|
*/
|
|
16117
16350
|
bump() {
|
|
16118
16351
|
if (this.#refs === 0) this.#arm();
|
|
@@ -16156,10 +16389,9 @@ function runDaemon(env, deps = {}) {
|
|
|
16156
16389
|
});
|
|
16157
16390
|
const bridge = new Bridge({
|
|
16158
16391
|
send: (frame) => http.send(frame),
|
|
16159
|
-
// 🔴
|
|
16160
|
-
//
|
|
16161
|
-
//
|
|
16162
|
-
// 우리가 하지 않으면 그 설치는 영구히 막힌다.
|
|
16392
|
+
// 🔴 Step aside once our own token looks stale. This process is detached
|
|
16393
|
+
// and survives an app restart, and a replacement only takes over on a newer
|
|
16394
|
+
// version — so nothing outside can unblock the install if we do not.
|
|
16163
16395
|
onStaleToken: () => {
|
|
16164
16396
|
log("pairing token is stale; exiting so the next call can start fresh");
|
|
16165
16397
|
void http.close().finally(() => exit(0));
|
|
@@ -30754,17 +30986,14 @@ var BridgeToolDescriptorSchema2 = external_exports2.object({
|
|
|
30754
30986
|
/** JSON Schema for the arguments, passed to the host verbatim. */
|
|
30755
30987
|
inputSchema: external_exports2.record(external_exports2.string(), external_exports2.unknown()),
|
|
30756
30988
|
/**
|
|
30757
|
-
* MCP tool annotations, derived from the tool's tier.
|
|
30989
|
+
* MCP tool annotations, derived from the tool's tier. Optional by ABI rule.
|
|
30758
30990
|
*
|
|
30759
|
-
* Typed loosely
|
|
30760
|
-
*
|
|
30761
|
-
* every future hint a wire-contract change on a schema that binaries we
|
|
30762
|
-
* cannot reach must keep parsing.
|
|
30991
|
+
* Typed loosely because this end only forwards it; pinning the shape here
|
|
30992
|
+
* would make every future hint a wire-contract change.
|
|
30763
30993
|
*
|
|
30764
|
-
* 🔴 A server
|
|
30765
|
-
* .
|
|
30766
|
-
*
|
|
30767
|
-
* INSIDE `inputSchema`, which is forwarded verbatim.
|
|
30994
|
+
* 🔴 A server predating this field strips it, so annotations never arrive
|
|
30995
|
+
* there. Anything that must survive such a server goes inside the input
|
|
30996
|
+
* schema, which is forwarded verbatim.
|
|
30768
30997
|
*/
|
|
30769
30998
|
annotations: external_exports2.record(external_exports2.string(), external_exports2.unknown()).optional()
|
|
30770
30999
|
});
|
|
@@ -30777,19 +31006,15 @@ var HelloSchema2 = external_exports2.object({
|
|
|
30777
31006
|
/** Pairing token. Not auto-attached by the browser (unlike a cookie). */
|
|
30778
31007
|
token: external_exports2.string(),
|
|
30779
31008
|
/**
|
|
30780
|
-
* The extension's own id
|
|
30781
|
-
*
|
|
30782
|
-
*
|
|
30783
|
-
* newer servers verify identity from this field when the header is absent.
|
|
30784
|
-
* Optional so every binary that predates it still parses (see the ABI note).
|
|
31009
|
+
* The extension's own id. Additive: an extension service worker's privileged
|
|
31010
|
+
* loopback fetch sends no Origin header, so servers verify identity from this
|
|
31011
|
+
* field when the header is absent.
|
|
30785
31012
|
*/
|
|
30786
31013
|
extensionId: external_exports2.string().optional(),
|
|
30787
31014
|
/**
|
|
30788
|
-
* Feature flags this panel build understands
|
|
30789
|
-
*
|
|
30790
|
-
*
|
|
30791
|
-
* must treat absence as "do not use". Optional with a default so every
|
|
30792
|
-
* binary that predates the field still parses.
|
|
31015
|
+
* Feature flags this panel build understands. Additive evolution happens
|
|
31016
|
+
* HERE, not via a protocol bump: a capability an old peer never sent is
|
|
31017
|
+
* simply absent, and code must treat absence as "do not use".
|
|
30793
31018
|
*/
|
|
30794
31019
|
capabilities: external_exports2.array(external_exports2.string()).default([])
|
|
30795
31020
|
});
|
|
@@ -30871,7 +31096,7 @@ var REQUEST_DEADLINE_MS2 = 9e4;
|
|
|
30871
31096
|
var EXEC_RESERVE_MS2 = 3e4;
|
|
30872
31097
|
var CONFIRM_TIMEOUT_MS2 = REQUEST_DEADLINE_MS2 - EXEC_RESERVE_MS2;
|
|
30873
31098
|
var READ_ONLY_TOOLS2 = [
|
|
30874
|
-
// --- Search-ad estimates. Uses
|
|
31099
|
+
// --- Search-ad estimates. Uses the user's own advertiser API credentials. ---
|
|
30875
31100
|
"ad_average_position_bid",
|
|
30876
31101
|
"ad_competition_density",
|
|
30877
31102
|
"ad_estimate_bid",
|
|
@@ -30926,7 +31151,7 @@ var READ_ONLY_TOOLS2 = [
|
|
|
30926
31151
|
"local_job_categories",
|
|
30927
31152
|
"local_region_trend",
|
|
30928
31153
|
"local_regions",
|
|
30929
|
-
// ---
|
|
31154
|
+
// --- The user's own channel analytics ---
|
|
30930
31155
|
"daily_trend",
|
|
30931
31156
|
"my_audience",
|
|
30932
31157
|
"my_blog_summary",
|
|
@@ -30955,7 +31180,7 @@ var READ_ONLY_TOOLS2 = [
|
|
|
30955
31180
|
"my_soaring",
|
|
30956
31181
|
"my_top_content",
|
|
30957
31182
|
"my_traffic_series",
|
|
30958
|
-
// --- Public place data, and reads of a place
|
|
31183
|
+
// --- Public place data, and reads of a place the user owns ---
|
|
30959
31184
|
"place_ai_briefing",
|
|
30960
31185
|
"place_announcements",
|
|
30961
31186
|
"place_autocomplete",
|
|
@@ -30987,8 +31212,8 @@ var READ_ONLY_TOOLS2 = [
|
|
|
30987
31212
|
"search_influencer",
|
|
30988
31213
|
"search_kin",
|
|
30989
31214
|
"search_news",
|
|
30990
|
-
//
|
|
30991
|
-
//
|
|
31215
|
+
// Referenced by search_blog's description, so omitting it would leave a
|
|
31216
|
+
// dangling reference for hosts.
|
|
30992
31217
|
"search_total",
|
|
30993
31218
|
"search_video",
|
|
30994
31219
|
"search_web",
|
|
@@ -31012,47 +31237,37 @@ var READ_ONLY_TOOLS2 = [
|
|
|
31012
31237
|
// Its siblings that suggest titles, outlines, or specs all call a language
|
|
31013
31238
|
// model, and are excluded for that reason.
|
|
31014
31239
|
"seo_scorecard",
|
|
31015
|
-
// ---
|
|
31016
|
-
// 🔴
|
|
31017
|
-
//
|
|
31018
|
-
//
|
|
31019
|
-
// 모델 호출이 0 이다 — 제외의 근거가 사라졌다.
|
|
31020
|
-
//
|
|
31021
|
-
// 읽고 제안만 하므로 여기(읽기 전용)가 맞다. 되돌릴 것이 없다.
|
|
31240
|
+
// --- Planning tools that need a model, but never call one from here ---
|
|
31241
|
+
// 🔴 The calling host already ran its own model, so calling the user's too
|
|
31242
|
+
// would bill one turn twice. These return prompt material and make no model
|
|
31243
|
+
// call, which is what lets them read and suggest here, undoing nothing.
|
|
31022
31244
|
"outline_suggest",
|
|
31023
31245
|
"seo_aeo_geo_spec",
|
|
31024
|
-
//
|
|
31025
|
-
//
|
|
31026
|
-
//
|
|
31027
|
-
// 항목만 쓰므로 blogId·sourcePostIds·sourceTitles 는 나가지 않는다.
|
|
31246
|
+
// Two-stage (analyse, then draft), so it returns the first stage's prompt and
|
|
31247
|
+
// the second only once the host hands the result back. The
|
|
31248
|
+
// style profile travels as prose inside that prompt, not as an object.
|
|
31028
31249
|
"write_draft",
|
|
31029
31250
|
"benchmark_gap",
|
|
31030
31251
|
"content_calendar",
|
|
31031
31252
|
"internal_links",
|
|
31032
31253
|
// --- Grouped with the writing tools, but a pure collector. ---
|
|
31033
|
-
//
|
|
31034
|
-
//
|
|
31035
|
-
// admitted after checking what it actually does. Classify by behaviour, not
|
|
31036
|
-
// by category.
|
|
31254
|
+
// Calls no model itself — it only gathers public data. Admitted after
|
|
31255
|
+
// checking behaviour rather than trusting the category label.
|
|
31037
31256
|
"run_research"
|
|
31038
31257
|
];
|
|
31039
31258
|
var ALLOWED2 = Object.freeze(new Set(READ_ONLY_TOOLS2));
|
|
31040
31259
|
var EXCLUDED_TOOLS2 = Object.freeze({
|
|
31041
|
-
// 🔴 editor_* 는 더 이상 여기 없다 — 등급 표(TOOL_TIERS)로 옮겼다. 제외였던
|
|
31042
|
-
// 이유는 "확인을 받을 방법이 없고 되돌릴 수단도 없다" 였는데, 둘 다 생겼다:
|
|
31043
|
-
// 세션 동의 + 호출별 확인이 서고, 주입 전후로 임시저장을 부르며 그것이
|
|
31044
|
-
// 거절될 때를 위해 문서 사본을 따로 뜬다.
|
|
31045
31260
|
generate_images: "writes to your post, needs confirmation, and spends your AI credits",
|
|
31046
|
-
// 🔴 editor_read / editor_read_structure 도 더 이상 여기 없다. 보류 사유는 "탭이
|
|
31047
|
-
// 열려 있어야 하는데 외부 에이전트에겐 열 탭이 없다" 였지만, 그것은 제외할 이유가
|
|
31048
|
-
// 아니라 **읽고 나서 알게 될 사실**이다 — 탭이 없으면 도구가 그렇게 답하면 된다.
|
|
31049
|
-
// 지금은 TOOL_TIERS 에서 read 등급으로 노출된다.
|
|
31050
|
-
// Spend your own AI credits on a turn the calling host already paid for.
|
|
31051
31261
|
// Side effect outside the request/response contract.
|
|
31052
|
-
export_report: "triggers a file download"
|
|
31262
|
+
export_report: "triggers a file download",
|
|
31263
|
+
// 🔴 A host that can generate its own image has a free way onto the canvas —
|
|
31264
|
+
// gallery_image_add. Spending credits for what the caller already has is the
|
|
31265
|
+
// charge nobody meant to make.
|
|
31266
|
+
photo_ai_image_add: "spends your AI credits for an image your own app can make and hand over",
|
|
31267
|
+
video_image_realign: "spends your AI credits per photo to reach what the free scene tools already reach"
|
|
31053
31268
|
});
|
|
31054
31269
|
var TOOL_TIERS2 = Object.freeze({
|
|
31055
|
-
// ──
|
|
31270
|
+
// ── Photo editor (our own surface) ──────────────────────────────
|
|
31056
31271
|
photo_canvas_get: "read",
|
|
31057
31272
|
photo_gallery_list: "read",
|
|
31058
31273
|
photo_text_add: "write",
|
|
@@ -31069,13 +31284,21 @@ var TOOL_TIERS2 = Object.freeze({
|
|
|
31069
31284
|
photo_node_remove: "destructive",
|
|
31070
31285
|
photo_page_remove: "destructive",
|
|
31071
31286
|
photo_template_apply: "destructive",
|
|
31072
|
-
|
|
31073
|
-
//
|
|
31287
|
+
// ── Gallery: the free way in ────────────────────────────────────
|
|
31288
|
+
//
|
|
31289
|
+
// 🔴 Destructive because it reads a path off the user's disk, the same
|
|
31290
|
+
// capability editor_insert_image carries. A session toggle would open the
|
|
31291
|
+
// whole filesystem once and stay open.
|
|
31292
|
+
gallery_image_add: "destructive",
|
|
31293
|
+
// ── Video editor (our own surface) ──────────────────────────────
|
|
31074
31294
|
video_timeline_get: "read",
|
|
31075
31295
|
video_sources_list: "read",
|
|
31076
31296
|
video_scene_add: "write",
|
|
31077
31297
|
video_subtitle_set: "write",
|
|
31078
31298
|
video_scene_image_set: "write",
|
|
31299
|
+
// Brings a gallery picture into this video — an add, like video_image_add.
|
|
31300
|
+
// The disk read already happened at gallery_image_add, under its own confirm.
|
|
31301
|
+
video_image_add_from_gallery: "write",
|
|
31079
31302
|
video_scene_update: "write",
|
|
31080
31303
|
video_background_set: "write",
|
|
31081
31304
|
video_canvas_resize: "write",
|
|
@@ -31087,52 +31310,39 @@ var TOOL_TIERS2 = Object.freeze({
|
|
|
31087
31310
|
video_node_remove: "destructive",
|
|
31088
31311
|
video_scene_remove: "destructive",
|
|
31089
31312
|
video_narration_generate: "paid",
|
|
31090
|
-
|
|
31091
|
-
// ── 스마트에디터 읽기 ────────────────────────────────────────────
|
|
31313
|
+
// ── Post editor: reads ──────────────────────────────────────────
|
|
31092
31314
|
editor_read: "read",
|
|
31093
31315
|
editor_read_structure: "read",
|
|
31094
|
-
// ──
|
|
31316
|
+
// ── Post editor: writes ─────────────────────────────────────────
|
|
31095
31317
|
//
|
|
31096
|
-
// 🔴
|
|
31097
|
-
//
|
|
31098
|
-
//
|
|
31099
|
-
// 프로세스에 본문 쓰기를 열 수는 없었다.
|
|
31318
|
+
// 🔴 Every injection is bracketed by a vendor autosave, plus a separate copy
|
|
31319
|
+
// of the document for the cases the vendor refuses one. Without that
|
|
31320
|
+
// recovery point, editor undo would be the only way back.
|
|
31100
31321
|
//
|
|
31101
|
-
// 🔴
|
|
31102
|
-
//
|
|
31322
|
+
// 🔴 Only whole-body replacement is destructive: the others append, so the
|
|
31323
|
+
// autosave point still holds, while replacing the body overwrites it.
|
|
31103
31324
|
editor_insert_draft: "write",
|
|
31104
|
-
//
|
|
31105
|
-
//
|
|
31106
|
-
//
|
|
31325
|
+
// Opening a window edits no document, but it does create a tab in the user's
|
|
31326
|
+
// browser, and a session that has not enabled editing can do nothing with the
|
|
31327
|
+
// window anyway — one tier keeps it one thing for the user to allow.
|
|
31107
31328
|
editor_open_window: "write",
|
|
31108
|
-
// 🔴
|
|
31109
|
-
//
|
|
31110
|
-
// 없앨 수 있다.
|
|
31329
|
+
// 🔴 Closing a window cannot be undone and the user's draft may be inside, so
|
|
31330
|
+
// it is asked per call rather than covered by a forgotten session toggle.
|
|
31111
31331
|
editor_reduce_windows: "destructive",
|
|
31112
|
-
// 🔴
|
|
31113
|
-
//
|
|
31332
|
+
// 🔴 Insertion appends, so it never passes through the overwrite prompt; the
|
|
31333
|
+
// tier is the only barrier, and the user approves each image's source.
|
|
31114
31334
|
editor_insert_image: "destructive",
|
|
31115
31335
|
editor_set_title: "write",
|
|
31116
|
-
// 🔴
|
|
31117
|
-
//
|
|
31118
|
-
// `editor_set_title` 과 같은 등급이다. 이름에 표면 접두가 없는 것은 이 도구가
|
|
31119
|
-
// 사진/영상 편집기 표면에 속하지 않아서다(접두 규약은 그 둘의 이름 충돌 방지용).
|
|
31336
|
+
// 🔴 Not an editor tool, but tiered like one: picking a title candidate IS
|
|
31337
|
+
// the confirmation and overwrites the title, so it matches editor_set_title.
|
|
31120
31338
|
title_optimize: "write",
|
|
31121
31339
|
editor_undo: "write",
|
|
31122
31340
|
editor_replace: "destructive"
|
|
31123
31341
|
});
|
|
31124
31342
|
var PAID_TOOL_COSTS2 = Object.freeze({
|
|
31125
|
-
photo_ai_image_add: Object.freeze({
|
|
31126
|
-
basis: "AI \uC774\uBBF8\uC9C0 1\uC7A5 \uC0DD\uC131",
|
|
31127
|
-
editorAsksAgain: false
|
|
31128
|
-
}),
|
|
31129
31343
|
video_narration_generate: Object.freeze({
|
|
31130
31344
|
basis: "\uBAA9\uC18C\uB9AC\uAC00 \uC5C6\uB294 \uC7A5\uBA74 \uC218\uB9CC\uD07C \uC74C\uC131 \uC0DD\uC131",
|
|
31131
31345
|
editorAsksAgain: true
|
|
31132
|
-
}),
|
|
31133
|
-
video_image_realign: Object.freeze({
|
|
31134
|
-
basis: "\uC0AC\uC9C4 \uC218\uB9CC\uD07C \uC774\uBBF8\uC9C0 \uBD84\uC11D",
|
|
31135
|
-
editorAsksAgain: true
|
|
31136
31346
|
})
|
|
31137
31347
|
});
|
|
31138
31348
|
var HINTS_BY_TIER2 = Object.freeze({
|
|
@@ -31162,9 +31372,9 @@ var LOOKUP_ANNOTATIONS2 = Object.freeze({
|
|
|
31162
31372
|
idempotentHint: true
|
|
31163
31373
|
});
|
|
31164
31374
|
var BRIDGE_LIMITS2 = Object.freeze({
|
|
31165
|
-
/**
|
|
31375
|
+
/** Calls available in one burst. */
|
|
31166
31376
|
capacity: 10,
|
|
31167
|
-
/**
|
|
31377
|
+
/** Slots refilled per second. */
|
|
31168
31378
|
refillPerSecond: 1
|
|
31169
31379
|
});
|
|
31170
31380
|
var CONNECTOR_RESULT_CAP_BYTES2 = 4 * 1024 * 1024;
|
|
@@ -31173,7 +31383,7 @@ var RESULT_BUDGET_BYTES2 = Math.floor(
|
|
|
31173
31383
|
CONNECTOR_RESULT_CAP_BYTES2 / RESULT_HEADROOM2
|
|
31174
31384
|
);
|
|
31175
31385
|
var REASON_POLICY2 = Object.freeze({
|
|
31176
|
-
// ---
|
|
31386
|
+
// --- Resolves on its own with time ---
|
|
31177
31387
|
rate_limited: Object.freeze({
|
|
31178
31388
|
retryable: true,
|
|
31179
31389
|
retryAfterMs: Math.ceil(1e3 / BRIDGE_LIMITS2.refillPerSecond)
|
|
@@ -31181,20 +31391,20 @@ var REASON_POLICY2 = Object.freeze({
|
|
|
31181
31391
|
timeout: Object.freeze({ retryable: true }),
|
|
31182
31392
|
connection_lost: Object.freeze({ retryable: true }),
|
|
31183
31393
|
tool_error: Object.freeze({ retryable: true }),
|
|
31184
|
-
// ---
|
|
31394
|
+
// --- The request itself must change ---
|
|
31185
31395
|
unknown_tool: Object.freeze({ retryable: false }),
|
|
31186
31396
|
invalid_args: Object.freeze({ retryable: false }),
|
|
31187
31397
|
tool_closed: Object.freeze({ retryable: false }),
|
|
31188
31398
|
missing_in_build: Object.freeze({ retryable: false }),
|
|
31189
|
-
// --- 🔴
|
|
31399
|
+
// --- 🔴 A human must act; re-calling only nags them ---
|
|
31190
31400
|
needs_consent: Object.freeze({ retryable: false }),
|
|
31191
31401
|
not_confirmed: Object.freeze({ retryable: false }),
|
|
31192
31402
|
confirm_timeout: Object.freeze({ retryable: false }),
|
|
31193
31403
|
confirm_too_late: Object.freeze({ retryable: false }),
|
|
31194
|
-
// ---
|
|
31404
|
+
// --- The user must do something in the browser ---
|
|
31195
31405
|
multiple_editors: Object.freeze({ retryable: false }),
|
|
31196
31406
|
editor_busy: Object.freeze({ retryable: false }),
|
|
31197
|
-
// ---
|
|
31407
|
+
// --- The connection itself must be repaired ---
|
|
31198
31408
|
not_connected: Object.freeze({ retryable: false }),
|
|
31199
31409
|
unauthorized: Object.freeze({ retryable: false })
|
|
31200
31410
|
});
|
|
@@ -31206,12 +31416,9 @@ var BRIDGE_USER_MESSAGES2 = Object.freeze({
|
|
|
31206
31416
|
/** The session hit the per-session rate limit. */
|
|
31207
31417
|
rateLimited: "\uC694\uCCAD\uC774 \uB108\uBB34 \uC7A6\uC544\uC694. \uC7A0\uAE50 \uAE30\uB2E4\uB838\uB2E4\uAC00 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
|
|
31208
31418
|
/**
|
|
31209
|
-
* The local connector accepted nothing back inside its ceiling.
|
|
31210
|
-
*
|
|
31211
|
-
*
|
|
31212
|
-
* which does not help when the browser side is fine and the connector is the
|
|
31213
|
-
* part that stopped. Pointing at the wrong thing costs them a round of
|
|
31214
|
-
* checking before they can even start fixing it.
|
|
31419
|
+
* The local connector accepted nothing back inside its ceiling. Distinct
|
|
31420
|
+
* from panelClosed: that one sends the user to Chrome, which does not help
|
|
31421
|
+
* when the browser side is fine and the connector is what stopped.
|
|
31215
31422
|
*/
|
|
31216
31423
|
connectorStuck: "\uC5F0\uACB0 \uD504\uB85C\uADF8\uB7A8\uC774 \uC751\uB2F5\uD558\uC9C0 \uC54A\uC544\uC694. \uC7A0\uC2DC \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uACE0, \uACC4\uC18D \uADF8\uB7EC\uBA74 \uD06C\uB86C \uD328\uB110\uC5D0\uC11C MCP \uC5F0\uACB0\uC744 \uAED0\uB2E4\uAC00 \uB2E4\uC2DC \uCF1C \uC8FC\uC138\uC694."
|
|
31217
31424
|
});
|
|
@@ -31383,9 +31590,9 @@ function createAdapterServer(deps = {}) {
|
|
|
31383
31590
|
const mcpUrl = `http://${host}:${port}/mcp`;
|
|
31384
31591
|
const server = new Server(
|
|
31385
31592
|
{ name: deps.name ?? NAME2, version: deps.version ?? "0.0.0" },
|
|
31386
|
-
//
|
|
31387
|
-
//
|
|
31388
|
-
//
|
|
31593
|
+
// Declares that we send list-changed notifications: the daemon signals us
|
|
31594
|
+
// when the panel connects or drops, and forwarding it lets the host
|
|
31595
|
+
// re-fetch the catalog without a restart.
|
|
31389
31596
|
{ capabilities: { tools: { listChanged: true } } }
|
|
31390
31597
|
);
|
|
31391
31598
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|