@noodleseed/one 0.89.1 → 0.90.0
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/dist/commands/billing-enforcement-activation-ops.js +1 -11
- package/dist/commands/billing-enforcement-activation-ops.js.map +1 -1
- package/dist/commands/billing-enforcement-ops.js +1 -11
- package/dist/commands/billing-enforcement-ops.js.map +1 -1
- package/dist/commands/billing-metering-ops.js +1 -11
- package/dist/commands/billing-metering-ops.js.map +1 -1
- package/dist/commands/billing-metering-validation-ops.js +1 -11
- package/dist/commands/billing-metering-validation-ops.js.map +1 -1
- package/dist/commands/billing-read-ops.js +1 -11
- package/dist/commands/billing-read-ops.js.map +1 -1
- package/dist/commands/billing-stripe-ops.js +1 -11
- package/dist/commands/billing-stripe-ops.js.map +1 -1
- package/dist/commands/org-admin.d.ts +6 -0
- package/dist/commands/org-admin.d.ts.map +1 -1
- package/dist/commands/org-admin.js +1 -29
- package/dist/commands/org-admin.js.map +1 -1
- package/dist/commands/org-domains.js +1 -22
- package/dist/commands/org-domains.js.map +1 -1
- package/dist/commands/org-member-args.d.ts +1 -1
- package/dist/commands/org-member-args.d.ts.map +1 -1
- package/dist/commands/org-member-args.js +1 -10
- package/dist/commands/org-member-args.js.map +1 -1
- package/dist/commands/org-openai-challenge.js +1 -22
- package/dist/commands/org-openai-challenge.js.map +1 -1
- package/dist/commands/platform-account-reset-args.d.ts.map +1 -1
- package/dist/commands/platform-account-reset-args.js +1 -6
- package/dist/commands/platform-account-reset-args.js.map +1 -1
- package/dist/commands/platform-account-reset-ops.js +1 -11
- package/dist/commands/platform-account-reset-ops.js.map +1 -1
- package/dist/commands/platform-auth-migration-args.d.ts +2 -0
- package/dist/commands/platform-auth-migration-args.d.ts.map +1 -1
- package/dist/commands/platform-auth-migration-args.js +2 -1
- package/dist/commands/platform-auth-migration-args.js.map +1 -1
- package/dist/commands/platform-auth-migration-ops.js +1 -11
- package/dist/commands/platform-auth-migration-ops.js.map +1 -1
- package/dist/commands/shared.d.ts +6 -0
- package/dist/commands/shared.d.ts.map +1 -1
- package/dist/commands/shared.js +34 -0
- package/dist/commands/shared.js.map +1 -1
- package/dist/deploy.d.ts +2 -0
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +4 -4
- package/dist/deploy.js.map +1 -1
- package/dist/dev.d.ts.map +1 -1
- package/dist/dev.js +1 -7
- package/dist/dev.js.map +1 -1
- package/dist/devtools-brand.d.ts +14 -0
- package/dist/devtools-brand.d.ts.map +1 -0
- package/dist/devtools-brand.js +98 -0
- package/dist/devtools-brand.js.map +1 -0
- package/dist/devtools-chat-anthropic.d.ts +3 -0
- package/dist/devtools-chat-anthropic.d.ts.map +1 -0
- package/dist/devtools-chat-anthropic.js +75 -0
- package/dist/devtools-chat-anthropic.js.map +1 -0
- package/dist/devtools-chat-gemini.d.ts +3 -0
- package/dist/devtools-chat-gemini.d.ts.map +1 -0
- package/dist/devtools-chat-gemini.js +82 -0
- package/dist/devtools-chat-gemini.js.map +1 -0
- package/dist/devtools-chat-types.d.ts +64 -0
- package/dist/devtools-chat-types.d.ts.map +1 -0
- package/dist/devtools-chat-types.js +64 -0
- package/dist/devtools-chat-types.js.map +1 -0
- package/dist/devtools-chat.d.ts +7 -39
- package/dist/devtools-chat.d.ts.map +1 -1
- package/dist/devtools-chat.js +33 -48
- package/dist/devtools-chat.js.map +1 -1
- package/dist/devtools-copy-styles.d.ts +3 -0
- package/dist/devtools-copy-styles.d.ts.map +1 -0
- package/dist/devtools-copy-styles.js +54 -0
- package/dist/devtools-copy-styles.js.map +1 -0
- package/dist/devtools-design.d.ts +5 -0
- package/dist/devtools-design.d.ts.map +1 -0
- package/dist/devtools-design.js +62 -0
- package/dist/devtools-design.js.map +1 -0
- package/dist/devtools-harness.d.ts +2 -2
- package/dist/devtools-harness.d.ts.map +1 -1
- package/dist/devtools-harness.js +130 -216
- package/dist/devtools-harness.js.map +1 -1
- package/dist/devtools-json.d.ts +10 -0
- package/dist/devtools-json.d.ts.map +1 -0
- package/dist/devtools-json.js +23 -0
- package/dist/devtools-json.js.map +1 -0
- package/dist/devtools-preview-control-styles.d.ts +3 -0
- package/dist/devtools-preview-control-styles.d.ts.map +1 -0
- package/dist/devtools-preview-control-styles.js +207 -0
- package/dist/devtools-preview-control-styles.js.map +1 -0
- package/dist/devtools-preview.d.ts +7 -3
- package/dist/devtools-preview.d.ts.map +1 -1
- package/dist/devtools-preview.js +189 -43
- package/dist/devtools-preview.js.map +1 -1
- package/dist/devtools-provider-logos.d.ts +5 -0
- package/dist/devtools-provider-logos.d.ts.map +1 -0
- package/dist/devtools-provider-logos.js +5 -0
- package/dist/devtools-provider-logos.js.map +1 -0
- package/dist/devtools-responsive-styles.d.ts +3 -0
- package/dist/devtools-responsive-styles.d.ts.map +1 -0
- package/dist/devtools-responsive-styles.js +75 -0
- package/dist/devtools-responsive-styles.js.map +1 -0
- package/dist/devtools-styles.d.ts +3 -0
- package/dist/devtools-styles.d.ts.map +1 -0
- package/dist/devtools-styles.js +694 -0
- package/dist/devtools-styles.js.map +1 -0
- package/dist/local-target.d.ts.map +1 -1
- package/dist/local-target.js +1 -6
- package/dist/local-target.js.map +1 -1
- package/dist/plugin-mode/build-readiness-server.d.ts +4 -10
- package/dist/plugin-mode/build-readiness-server.d.ts.map +1 -1
- package/dist/plugin-mode/build-readiness-server.js +68 -67
- package/dist/plugin-mode/build-readiness-server.js.map +1 -1
- package/dist/plugin-mode/plugin-command-runner.d.ts +41 -0
- package/dist/plugin-mode/plugin-command-runner.d.ts.map +1 -0
- package/dist/plugin-mode/plugin-command-runner.js +122 -0
- package/dist/plugin-mode/plugin-command-runner.js.map +1 -0
- package/dist/plugin-mode/plugin-operation-contract.d.ts +237 -0
- package/dist/plugin-mode/plugin-operation-contract.d.ts.map +1 -0
- package/dist/plugin-mode/plugin-operation-contract.js +164 -0
- package/dist/plugin-mode/plugin-operation-contract.js.map +1 -0
- package/dist/plugin-mode/plugin-operation-tools.d.ts +20 -0
- package/dist/plugin-mode/plugin-operation-tools.d.ts.map +1 -0
- package/dist/plugin-mode/plugin-operation-tools.js +289 -0
- package/dist/plugin-mode/plugin-operation-tools.js.map +1 -0
- package/dist/preview-session.d.ts.map +1 -1
- package/dist/preview-session.js +22 -5
- package/dist/preview-session.js.map +1 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +1 -7
- package/dist/project.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/plugin-bundle.d.ts.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/plugin-bundle.js +16 -12
- package/node_modules/@noodle-borg/agent-kit/dist/plugin-bundle.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/plugin-marketplace-docs.js +9 -9
- package/node_modules/@noodle-borg/agent-kit/dist/skill-feedback-ref.js +4 -4
- package/node_modules/@noodle-borg/agent-kit/dist/skill-feedback-ref.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-router.d.ts.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-router.js +6 -3
- package/node_modules/@noodle-borg/agent-kit/dist/skill-router.js.map +1 -1
- package/node_modules/@noodle-borg/connector-defs/dist/index.d.ts +1 -1
- package/node_modules/@noodle-borg/connector-defs/dist/index.d.ts.map +1 -1
- package/node_modules/@noodle-borg/connector-defs/dist/index.js.map +1 -1
- package/node_modules/@noodle-borg/module-audit/dist/route.d.ts +0 -2
- package/node_modules/@noodle-borg/module-audit/dist/route.d.ts.map +1 -1
- package/node_modules/@noodle-borg/module-audit/dist/route.js +0 -4
- package/node_modules/@noodle-borg/module-audit/dist/route.js.map +1 -1
- package/node_modules/@noodle-borg/protocol/dist/input-validation.d.ts +0 -2
- package/node_modules/@noodle-borg/protocol/dist/input-validation.d.ts.map +1 -1
- package/node_modules/@noodle-borg/protocol/dist/input-validation.js +1 -5
- package/node_modules/@noodle-borg/protocol/dist/input-validation.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/auth/deploy-gate.d.ts +0 -2
- package/node_modules/@noodle-borg/service/dist/auth/deploy-gate.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/auth/deploy-gate.js +0 -7
- package/node_modules/@noodle-borg/service/dist/auth/deploy-gate.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/credential-binding-index.d.ts +1 -3
- package/node_modules/@noodle-borg/service/dist/credential-binding-index.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/credential-binding-index.js +1 -14
- package/node_modules/@noodle-borg/service/dist/credential-binding-index.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/index.d.ts +5 -5
- package/node_modules/@noodle-borg/service/dist/index.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/index.js +1 -1
- package/node_modules/@noodle-borg/service/dist/index.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/mcp-public-routing.d.ts +0 -2
- package/node_modules/@noodle-borg/service/dist/mcp-public-routing.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/mcp-public-routing.js +0 -6
- package/node_modules/@noodle-borg/service/dist/mcp-public-routing.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/oauth/console-url.d.ts +2 -0
- package/node_modules/@noodle-borg/service/dist/oauth/console-url.d.ts.map +1 -0
- package/node_modules/@noodle-borg/service/dist/oauth/console-url.js +4 -0
- package/node_modules/@noodle-borg/service/dist/oauth/console-url.js.map +1 -0
- package/node_modules/@noodle-borg/service/dist/oauth/service-bootstrap.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/oauth/service-bootstrap.js +2 -1
- package/node_modules/@noodle-borg/service/dist/oauth/service-bootstrap.js.map +1 -1
- package/node_modules/@noodle-borg/service/dist/registry-targets.d.ts +1 -3
- package/node_modules/@noodle-borg/service/dist/registry-targets.d.ts.map +1 -1
- package/node_modules/@noodle-borg/service/dist/registry-targets.js +0 -17
- package/node_modules/@noodle-borg/service/dist/registry-targets.js.map +1 -1
- package/node_modules/@noodle-borg/service/package.json +0 -1
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Syntax colours for structured output in preview, chat tool calls, and the MCP activity rail. */
|
|
2
|
+
export declare const DEVTOOLS_JSON_STYLES: string;
|
|
3
|
+
/**
|
|
4
|
+
* A dependency-free JSON renderer for the classic devtools client.
|
|
5
|
+
*
|
|
6
|
+
* Values are first serialized with native JSON formatting, then lexed into text-only spans. Tool data
|
|
7
|
+
* never reaches `innerHTML`, so strings such as `</script>` and `<img onerror=…>` remain inert output.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEVTOOLS_JSON_CLIENT_JS: string;
|
|
10
|
+
//# sourceMappingURL=devtools-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools-json.d.ts","sourceRoot":"","sources":["../src/devtools-json.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,eAAO,MAAM,oBAAoB,QAQhC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,QAKxB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Syntax colours for structured output in preview, chat tool calls, and the MCP activity rail. */
|
|
2
|
+
export const DEVTOOLS_JSON_STYLES = String.raw `
|
|
3
|
+
.json-key{color:#F97316}
|
|
4
|
+
.json-string{color:#F5C0A8}
|
|
5
|
+
.json-number{color:#F59E0B}
|
|
6
|
+
.json-literal{color:#F43F5E}
|
|
7
|
+
.json-punctuation{color:#7C7470}
|
|
8
|
+
.json-fallback{color:#EDE8E6}
|
|
9
|
+
.json-error{color:#FB7185}
|
|
10
|
+
`;
|
|
11
|
+
/**
|
|
12
|
+
* A dependency-free JSON renderer for the classic devtools client.
|
|
13
|
+
*
|
|
14
|
+
* Values are first serialized with native JSON formatting, then lexed into text-only spans. Tool data
|
|
15
|
+
* never reaches `innerHTML`, so strings such as `</script>` and `<img onerror=…>` remain inert output.
|
|
16
|
+
*/
|
|
17
|
+
export const DEVTOOLS_JSON_CLIENT_JS = [
|
|
18
|
+
'function jsonSource(value){ if(typeof value==="string"){ var trimmed=value.trim(); if((trimmed.charAt(0)==="{"&&trimmed.charAt(trimmed.length-1)==="}")||(trimmed.charAt(0)==="["&&trimmed.charAt(trimmed.length-1)==="]")){ try{return JSON.stringify(JSON.parse(value),null,2);}catch(e){} } return value; } try{ var encoded=JSON.stringify(value,null,2); return encoded===undefined?String(value):encoded; }catch(e){ return String(value); } }',
|
|
19
|
+
'function jsonClass(token,source,end){ if(token.charAt(0)==="\\""){ return /^\\s*:/.test(source.slice(end))?"json-key":"json-string"; } if(/^-?\\d/.test(token)) return "json-number"; if(/^(true|false|null)$/.test(token)) return "json-literal"; return "json-punctuation"; }',
|
|
20
|
+
'function appendJsonText(parent,text,cls){ if(!text) return; if(!cls){ parent.appendChild(document.createTextNode(text)); return; } var span=document.createElement("span"); span.className=cls; span.textContent=text; parent.appendChild(span); }',
|
|
21
|
+
'function renderJson(pre,value){ var source=jsonSource(value); var pattern=/"(?:\\\\.|[^"\\\\])*"|-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?|\\b(?:true|false|null)\\b|[{}\\[\\],:]/g; var fragment=document.createDocumentFragment(); var cursor=0; var match; while((match=pattern.exec(source))!==null){ appendJsonText(fragment,source.slice(cursor,match.index),""); appendJsonText(fragment,match[0],jsonClass(match[0],source,pattern.lastIndex)); cursor=pattern.lastIndex; } appendJsonText(fragment,source.slice(cursor),""); pre.replaceChildren(fragment); pre.setAttribute("aria-label","JSON output"); }',
|
|
22
|
+
].join('\n');
|
|
23
|
+
//# sourceMappingURL=devtools-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools-json.js","sourceRoot":"","sources":["../src/devtools-json.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;CAQ7C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,sbAAsb;IACtb,iRAAiR;IACjR,oPAAoP;IACpP,ilBAAilB;CACllB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Compact, direct preview controls for theme, device presets, width, reload, and fullscreen. */
|
|
2
|
+
export declare const DEVTOOLS_PREVIEW_CONTROL_STYLES = "\n.controls{\n display:flex;\n align-items:center;\n gap:9px;\n flex-wrap:wrap;\n margin-bottom:16px;\n padding:7px;\n border:0;\n border-radius:15px;\n background:rgba(255,255,255,.035);\n}\n.ctl{display:flex;align-items:center;gap:7px}\n.ctl__lbl{\n color:var(--nd-faint);\n font-size:9px;\n font-weight:650;\n letter-spacing:.09em;\n text-transform:uppercase;\n}\n.theme-switch,.device-switch{\n display:inline-flex;\n align-items:center;\n border:0;\n border-radius:999px;\n background:rgba(0,0,0,.34);\n}\n.theme-switch{\n gap:6px;\n min-height:31px;\n padding:4px 7px;\n color:var(--nd-faint);\n cursor:pointer;\n}\n.theme-switch:hover{color:var(--nd-text)}\n.theme-switch__icon{display:grid;width:14px;height:14px;place-items:center}\n.theme-switch__icon svg{width:13px;height:13px}\n.theme-switch__track{\n position:relative;\n width:27px;\n height:15px;\n border-radius:999px;\n background:rgba(255,255,255,.11);\n transition:background .18s ease;\n}\n.theme-switch__thumb{\n position:absolute;\n top:3px;\n left:3px;\n width:9px;\n height:9px;\n border-radius:50%;\n background:#f4f4f5;\n box-shadow:0 1px 5px rgba(0,0,0,.45);\n transition:transform .2s cubic-bezier(.2,.8,.2,1),background .18s ease;\n}\n.theme-switch[aria-pressed=true] .theme-switch__track{background:rgba(249,115,22,.32)}\n.theme-switch[aria-pressed=true] .theme-switch__thumb{\n background:#fdba74;\n transform:translateX(12px);\n}\n.device-switch{gap:2px;padding:3px}\n.device-switch button{\n display:grid;\n width:29px;\n height:25px;\n place-items:center;\n padding:0;\n border:0;\n border-radius:999px;\n background:transparent;\n color:var(--nd-faint);\n cursor:pointer;\n transition:background .18s ease,color .18s ease,transform .18s ease;\n}\n.device-switch button:hover{color:var(--nd-text);transform:translateY(-1px)}\n.device-switch button.is-active{\n background:rgba(255,255,255,.11);\n color:#fff;\n}\n.device-switch svg{width:14px;height:14px}\n.ctl--width{\n gap:10px;\n padding:3px 4px 3px 9px;\n border-radius:999px;\n background:rgba(0,0,0,.28);\n transition:background .18s ease,box-shadow .18s ease;\n}\n.ctl--width:hover{background:rgba(0,0,0,.42)}\n.ctl--width:focus-within{\n background:rgba(255,255,255,.055);\n box-shadow:0 8px 24px rgba(0,0,0,.18),0 0 20px rgba(249,115,22,.07);\n}\n.ctl--width input[type=range]{\n --width-progress:56.82%;\n appearance:none;\n -webkit-appearance:none;\n width:clamp(132px,12vw,184px);\n height:24px;\n margin:0;\n padding:0;\n border:0;\n background:transparent;\n cursor:ew-resize;\n}\n.ctl--width input[type=range]:focus-visible{background:transparent;box-shadow:none}\n.ctl--width input[type=range]::-webkit-slider-runnable-track{\n height:5px;\n border-radius:999px;\n background:\n linear-gradient(90deg,var(--nd-accent),var(--nd-rose)) 0/var(--width-progress) 100% no-repeat,\n rgba(255,255,255,.11);\n box-shadow:inset 0 1px 2px rgba(0,0,0,.55);\n}\n.ctl--width input[type=range]::-webkit-slider-thumb{\n appearance:none;\n -webkit-appearance:none;\n width:15px;\n height:15px;\n margin-top:-5px;\n border:0;\n border-radius:50%;\n background:#fafafa;\n box-shadow:\n 0 2px 8px rgba(0,0,0,.62),\n 0 0 0 4px rgba(249,115,22,.13),\n 0 0 14px rgba(244,63,94,.13);\n transition:transform .17s cubic-bezier(.2,.8,.2,1),box-shadow .17s ease;\n}\n.ctl--width input[type=range]::-moz-range-track{\n height:5px;\n border:0;\n border-radius:999px;\n background:rgba(255,255,255,.11);\n box-shadow:inset 0 1px 2px rgba(0,0,0,.55);\n}\n.ctl--width input[type=range]::-moz-range-progress{\n height:5px;\n border-radius:999px;\n background:linear-gradient(90deg,var(--nd-accent),var(--nd-rose));\n}\n.ctl--width input[type=range]::-moz-range-thumb{\n width:15px;\n height:15px;\n border:0;\n border-radius:50%;\n background:#fafafa;\n box-shadow:\n 0 2px 8px rgba(0,0,0,.62),\n 0 0 0 4px rgba(249,115,22,.13),\n 0 0 14px rgba(244,63,94,.13);\n transition:transform .17s cubic-bezier(.2,.8,.2,1),box-shadow .17s ease;\n}\n.ctl--width:hover input[type=range]::-webkit-slider-thumb,\n.ctl--width:focus-within input[type=range]::-webkit-slider-thumb{\n transform:scale(1.1);\n box-shadow:\n 0 3px 10px rgba(0,0,0,.7),\n 0 0 0 5px rgba(249,115,22,.17),\n 0 0 18px rgba(244,63,94,.2);\n}\n.ctl--width:hover input[type=range]::-moz-range-thumb,\n.ctl--width:focus-within input[type=range]::-moz-range-thumb{\n transform:scale(1.1);\n box-shadow:\n 0 3px 10px rgba(0,0,0,.7),\n 0 0 0 5px rgba(249,115,22,.17),\n 0 0 18px rgba(244,63,94,.2);\n}\n.ctl--width:active input[type=range]::-webkit-slider-thumb{transform:scale(.94)}\n.ctl--width:active input[type=range]::-moz-range-thumb{transform:scale(.94)}\n.ctl__val{\n min-width:57px;\n padding:5px 8px;\n border-radius:999px;\n background:rgba(255,255,255,.06);\n color:#e4e4e7;\n font-family:var(--nd-mono);\n font-size:10.5px;\n font-variant-numeric:tabular-nums;\n line-height:1;\n text-align:center;\n}\n@media (prefers-reduced-motion:reduce){\n .ctl--width input[type=range]::-webkit-slider-thumb,\n .ctl--width input[type=range]::-moz-range-thumb{transform:none!important}\n}\n.ctl__spacer{margin-left:auto}\n.ctl-btn{\n display:inline-flex;\n align-items:center;\n gap:6px;\n min-height:31px;\n padding:6px 10px;\n border:0;\n border-radius:9px;\n background:rgba(0,0,0,.34);\n color:var(--nd-muted);\n font-size:11px;\n font-weight:550;\n cursor:pointer;\n}\n.ctl-btn:hover{background:var(--nd-raised);color:var(--nd-text)}\n.ctl-btn svg{width:13px;height:13px}\n";
|
|
3
|
+
//# sourceMappingURL=devtools-preview-control-styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools-preview-control-styles.d.ts","sourceRoot":"","sources":["../src/devtools-preview-control-styles.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,eAAO,MAAM,+BAA+B,y/KA4M3C,CAAC"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/** Compact, direct preview controls for theme, device presets, width, reload, and fullscreen. */
|
|
2
|
+
export const DEVTOOLS_PREVIEW_CONTROL_STYLES = `
|
|
3
|
+
.controls{
|
|
4
|
+
display:flex;
|
|
5
|
+
align-items:center;
|
|
6
|
+
gap:9px;
|
|
7
|
+
flex-wrap:wrap;
|
|
8
|
+
margin-bottom:16px;
|
|
9
|
+
padding:7px;
|
|
10
|
+
border:0;
|
|
11
|
+
border-radius:15px;
|
|
12
|
+
background:rgba(255,255,255,.035);
|
|
13
|
+
}
|
|
14
|
+
.ctl{display:flex;align-items:center;gap:7px}
|
|
15
|
+
.ctl__lbl{
|
|
16
|
+
color:var(--nd-faint);
|
|
17
|
+
font-size:9px;
|
|
18
|
+
font-weight:650;
|
|
19
|
+
letter-spacing:.09em;
|
|
20
|
+
text-transform:uppercase;
|
|
21
|
+
}
|
|
22
|
+
.theme-switch,.device-switch{
|
|
23
|
+
display:inline-flex;
|
|
24
|
+
align-items:center;
|
|
25
|
+
border:0;
|
|
26
|
+
border-radius:999px;
|
|
27
|
+
background:rgba(0,0,0,.34);
|
|
28
|
+
}
|
|
29
|
+
.theme-switch{
|
|
30
|
+
gap:6px;
|
|
31
|
+
min-height:31px;
|
|
32
|
+
padding:4px 7px;
|
|
33
|
+
color:var(--nd-faint);
|
|
34
|
+
cursor:pointer;
|
|
35
|
+
}
|
|
36
|
+
.theme-switch:hover{color:var(--nd-text)}
|
|
37
|
+
.theme-switch__icon{display:grid;width:14px;height:14px;place-items:center}
|
|
38
|
+
.theme-switch__icon svg{width:13px;height:13px}
|
|
39
|
+
.theme-switch__track{
|
|
40
|
+
position:relative;
|
|
41
|
+
width:27px;
|
|
42
|
+
height:15px;
|
|
43
|
+
border-radius:999px;
|
|
44
|
+
background:rgba(255,255,255,.11);
|
|
45
|
+
transition:background .18s ease;
|
|
46
|
+
}
|
|
47
|
+
.theme-switch__thumb{
|
|
48
|
+
position:absolute;
|
|
49
|
+
top:3px;
|
|
50
|
+
left:3px;
|
|
51
|
+
width:9px;
|
|
52
|
+
height:9px;
|
|
53
|
+
border-radius:50%;
|
|
54
|
+
background:#f4f4f5;
|
|
55
|
+
box-shadow:0 1px 5px rgba(0,0,0,.45);
|
|
56
|
+
transition:transform .2s cubic-bezier(.2,.8,.2,1),background .18s ease;
|
|
57
|
+
}
|
|
58
|
+
.theme-switch[aria-pressed=true] .theme-switch__track{background:rgba(249,115,22,.32)}
|
|
59
|
+
.theme-switch[aria-pressed=true] .theme-switch__thumb{
|
|
60
|
+
background:#fdba74;
|
|
61
|
+
transform:translateX(12px);
|
|
62
|
+
}
|
|
63
|
+
.device-switch{gap:2px;padding:3px}
|
|
64
|
+
.device-switch button{
|
|
65
|
+
display:grid;
|
|
66
|
+
width:29px;
|
|
67
|
+
height:25px;
|
|
68
|
+
place-items:center;
|
|
69
|
+
padding:0;
|
|
70
|
+
border:0;
|
|
71
|
+
border-radius:999px;
|
|
72
|
+
background:transparent;
|
|
73
|
+
color:var(--nd-faint);
|
|
74
|
+
cursor:pointer;
|
|
75
|
+
transition:background .18s ease,color .18s ease,transform .18s ease;
|
|
76
|
+
}
|
|
77
|
+
.device-switch button:hover{color:var(--nd-text);transform:translateY(-1px)}
|
|
78
|
+
.device-switch button.is-active{
|
|
79
|
+
background:rgba(255,255,255,.11);
|
|
80
|
+
color:#fff;
|
|
81
|
+
}
|
|
82
|
+
.device-switch svg{width:14px;height:14px}
|
|
83
|
+
.ctl--width{
|
|
84
|
+
gap:10px;
|
|
85
|
+
padding:3px 4px 3px 9px;
|
|
86
|
+
border-radius:999px;
|
|
87
|
+
background:rgba(0,0,0,.28);
|
|
88
|
+
transition:background .18s ease,box-shadow .18s ease;
|
|
89
|
+
}
|
|
90
|
+
.ctl--width:hover{background:rgba(0,0,0,.42)}
|
|
91
|
+
.ctl--width:focus-within{
|
|
92
|
+
background:rgba(255,255,255,.055);
|
|
93
|
+
box-shadow:0 8px 24px rgba(0,0,0,.18),0 0 20px rgba(249,115,22,.07);
|
|
94
|
+
}
|
|
95
|
+
.ctl--width input[type=range]{
|
|
96
|
+
--width-progress:56.82%;
|
|
97
|
+
appearance:none;
|
|
98
|
+
-webkit-appearance:none;
|
|
99
|
+
width:clamp(132px,12vw,184px);
|
|
100
|
+
height:24px;
|
|
101
|
+
margin:0;
|
|
102
|
+
padding:0;
|
|
103
|
+
border:0;
|
|
104
|
+
background:transparent;
|
|
105
|
+
cursor:ew-resize;
|
|
106
|
+
}
|
|
107
|
+
.ctl--width input[type=range]:focus-visible{background:transparent;box-shadow:none}
|
|
108
|
+
.ctl--width input[type=range]::-webkit-slider-runnable-track{
|
|
109
|
+
height:5px;
|
|
110
|
+
border-radius:999px;
|
|
111
|
+
background:
|
|
112
|
+
linear-gradient(90deg,var(--nd-accent),var(--nd-rose)) 0/var(--width-progress) 100% no-repeat,
|
|
113
|
+
rgba(255,255,255,.11);
|
|
114
|
+
box-shadow:inset 0 1px 2px rgba(0,0,0,.55);
|
|
115
|
+
}
|
|
116
|
+
.ctl--width input[type=range]::-webkit-slider-thumb{
|
|
117
|
+
appearance:none;
|
|
118
|
+
-webkit-appearance:none;
|
|
119
|
+
width:15px;
|
|
120
|
+
height:15px;
|
|
121
|
+
margin-top:-5px;
|
|
122
|
+
border:0;
|
|
123
|
+
border-radius:50%;
|
|
124
|
+
background:#fafafa;
|
|
125
|
+
box-shadow:
|
|
126
|
+
0 2px 8px rgba(0,0,0,.62),
|
|
127
|
+
0 0 0 4px rgba(249,115,22,.13),
|
|
128
|
+
0 0 14px rgba(244,63,94,.13);
|
|
129
|
+
transition:transform .17s cubic-bezier(.2,.8,.2,1),box-shadow .17s ease;
|
|
130
|
+
}
|
|
131
|
+
.ctl--width input[type=range]::-moz-range-track{
|
|
132
|
+
height:5px;
|
|
133
|
+
border:0;
|
|
134
|
+
border-radius:999px;
|
|
135
|
+
background:rgba(255,255,255,.11);
|
|
136
|
+
box-shadow:inset 0 1px 2px rgba(0,0,0,.55);
|
|
137
|
+
}
|
|
138
|
+
.ctl--width input[type=range]::-moz-range-progress{
|
|
139
|
+
height:5px;
|
|
140
|
+
border-radius:999px;
|
|
141
|
+
background:linear-gradient(90deg,var(--nd-accent),var(--nd-rose));
|
|
142
|
+
}
|
|
143
|
+
.ctl--width input[type=range]::-moz-range-thumb{
|
|
144
|
+
width:15px;
|
|
145
|
+
height:15px;
|
|
146
|
+
border:0;
|
|
147
|
+
border-radius:50%;
|
|
148
|
+
background:#fafafa;
|
|
149
|
+
box-shadow:
|
|
150
|
+
0 2px 8px rgba(0,0,0,.62),
|
|
151
|
+
0 0 0 4px rgba(249,115,22,.13),
|
|
152
|
+
0 0 14px rgba(244,63,94,.13);
|
|
153
|
+
transition:transform .17s cubic-bezier(.2,.8,.2,1),box-shadow .17s ease;
|
|
154
|
+
}
|
|
155
|
+
.ctl--width:hover input[type=range]::-webkit-slider-thumb,
|
|
156
|
+
.ctl--width:focus-within input[type=range]::-webkit-slider-thumb{
|
|
157
|
+
transform:scale(1.1);
|
|
158
|
+
box-shadow:
|
|
159
|
+
0 3px 10px rgba(0,0,0,.7),
|
|
160
|
+
0 0 0 5px rgba(249,115,22,.17),
|
|
161
|
+
0 0 18px rgba(244,63,94,.2);
|
|
162
|
+
}
|
|
163
|
+
.ctl--width:hover input[type=range]::-moz-range-thumb,
|
|
164
|
+
.ctl--width:focus-within input[type=range]::-moz-range-thumb{
|
|
165
|
+
transform:scale(1.1);
|
|
166
|
+
box-shadow:
|
|
167
|
+
0 3px 10px rgba(0,0,0,.7),
|
|
168
|
+
0 0 0 5px rgba(249,115,22,.17),
|
|
169
|
+
0 0 18px rgba(244,63,94,.2);
|
|
170
|
+
}
|
|
171
|
+
.ctl--width:active input[type=range]::-webkit-slider-thumb{transform:scale(.94)}
|
|
172
|
+
.ctl--width:active input[type=range]::-moz-range-thumb{transform:scale(.94)}
|
|
173
|
+
.ctl__val{
|
|
174
|
+
min-width:57px;
|
|
175
|
+
padding:5px 8px;
|
|
176
|
+
border-radius:999px;
|
|
177
|
+
background:rgba(255,255,255,.06);
|
|
178
|
+
color:#e4e4e7;
|
|
179
|
+
font-family:var(--nd-mono);
|
|
180
|
+
font-size:10.5px;
|
|
181
|
+
font-variant-numeric:tabular-nums;
|
|
182
|
+
line-height:1;
|
|
183
|
+
text-align:center;
|
|
184
|
+
}
|
|
185
|
+
@media (prefers-reduced-motion:reduce){
|
|
186
|
+
.ctl--width input[type=range]::-webkit-slider-thumb,
|
|
187
|
+
.ctl--width input[type=range]::-moz-range-thumb{transform:none!important}
|
|
188
|
+
}
|
|
189
|
+
.ctl__spacer{margin-left:auto}
|
|
190
|
+
.ctl-btn{
|
|
191
|
+
display:inline-flex;
|
|
192
|
+
align-items:center;
|
|
193
|
+
gap:6px;
|
|
194
|
+
min-height:31px;
|
|
195
|
+
padding:6px 10px;
|
|
196
|
+
border:0;
|
|
197
|
+
border-radius:9px;
|
|
198
|
+
background:rgba(0,0,0,.34);
|
|
199
|
+
color:var(--nd-muted);
|
|
200
|
+
font-size:11px;
|
|
201
|
+
font-weight:550;
|
|
202
|
+
cursor:pointer;
|
|
203
|
+
}
|
|
204
|
+
.ctl-btn:hover{background:var(--nd-raised);color:var(--nd-text)}
|
|
205
|
+
.ctl-btn svg{width:13px;height:13px}
|
|
206
|
+
`;
|
|
207
|
+
//# sourceMappingURL=devtools-preview-control-styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools-preview-control-styles.js","sourceRoot":"","sources":["../src/devtools-preview-control-styles.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,MAAM,CAAC,MAAM,+BAA+B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4M9C,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { harnessHtml, openAiShimScript, type PreviewDevice, type PreviewTheme, wrapWidgetHtml } from './devtools-harness.js';
|
|
1
|
+
import { calculateRangeProgress, harnessHtml, openAiShimScript, type PreviewDevice, type PreviewTheme, wrapWidgetHtml } from './devtools-harness.js';
|
|
2
2
|
/**
|
|
3
3
|
* `noodle devtools` preview server. It boots alongside a `dev` MCP endpoint and:
|
|
4
4
|
* - serves the three-pane harness shell (HTML/CSS/JS in `devtools-harness.ts`),
|
|
@@ -9,15 +9,19 @@ import { harnessHtml, openAiShimScript, type PreviewDevice, type PreviewTheme, w
|
|
|
9
9
|
* `fetch('/rpc')` the harness proxies and records, without reimplementing the ext-apps host protocol.
|
|
10
10
|
*/
|
|
11
11
|
export type { PreviewDevice, PreviewTheme };
|
|
12
|
-
export { harnessHtml, openAiShimScript, wrapWidgetHtml };
|
|
12
|
+
export { calculateRangeProgress, harnessHtml, openAiShimScript, wrapWidgetHtml };
|
|
13
13
|
export interface PreviewOptions {
|
|
14
14
|
readonly mcpUrl: string;
|
|
15
15
|
readonly theme: PreviewTheme;
|
|
16
16
|
readonly device: PreviewDevice;
|
|
17
17
|
readonly port?: number;
|
|
18
18
|
readonly protocolVersion?: string;
|
|
19
|
-
/** OpenAI model for the chat playground; falls back to `OPENAI_MODEL` then {@link
|
|
19
|
+
/** OpenAI model for the chat playground; falls back to `OPENAI_MODEL` then {@link DEFAULT_CHAT_MODELS}. */
|
|
20
20
|
readonly openaiModel?: string;
|
|
21
|
+
/** Anthropic model for the chat playground; falls back to `ANTHROPIC_MODEL`. */
|
|
22
|
+
readonly anthropicModel?: string;
|
|
23
|
+
/** Gemini model for the chat playground; falls back to `GEMINI_MODEL`. */
|
|
24
|
+
readonly geminiModel?: string;
|
|
21
25
|
}
|
|
22
26
|
export interface RpcLogEntry {
|
|
23
27
|
readonly time: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devtools-preview.d.ts","sourceRoot":"","sources":["../src/devtools-preview.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"devtools-preview.d.ts","sourceRoot":"","sources":["../src/devtools-preview.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,sBAAsB,EACtB,WAAW,EACX,gBAAgB,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,cAAc,EACf,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;GAQG;AAEH,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;AAE5C,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAC;AAEjF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,2GAA2G;IAC3G,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAOD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,SAAS,WAAW,EAAE,CAAC;IACrC,4FAA4F;IAC5F,YAAY,IAAI,IAAI,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAuHD,wBAAsB,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAuclF"}
|
package/dist/devtools-preview.js
CHANGED
|
@@ -1,12 +1,41 @@
|
|
|
1
1
|
import { createServer } from 'node:http';
|
|
2
|
-
import {
|
|
3
|
-
import { harnessHtml, openAiShimScript, wrapWidgetHtml, } from './devtools-harness.js';
|
|
2
|
+
import { DEFAULT_CHAT_MODELS, runProviderChatTurn, } from './devtools-chat.js';
|
|
3
|
+
import { calculateRangeProgress, harnessHtml, openAiShimScript, wrapWidgetHtml, } from './devtools-harness.js';
|
|
4
4
|
// Re-exported so `devtools-preview` stays the single import surface for the command and tests.
|
|
5
|
-
export { harnessHtml, openAiShimScript, wrapWidgetHtml };
|
|
5
|
+
export { calculateRangeProgress, harnessHtml, openAiShimScript, wrapWidgetHtml };
|
|
6
6
|
const CHAT_SYSTEM_PROMPT = 'You are an assistant embedded in the Noodle Seed devtools playground. You can call the MCP tools ' +
|
|
7
7
|
'exposed by the local server the developer is previewing. Call a tool whenever it can answer the ' +
|
|
8
8
|
'request or render a widget, then explain the result briefly. Keep answers concise.';
|
|
9
9
|
const DEFAULT_PROTOCOL_VERSION = '2025-11-25';
|
|
10
|
+
const CHAT_PROVIDERS = ['openai', 'anthropic', 'gemini'];
|
|
11
|
+
const CHAT_PROVIDER_CONFIG = {
|
|
12
|
+
openai: {
|
|
13
|
+
envName: 'OPENAI_API_KEY',
|
|
14
|
+
modelEnvName: 'OPENAI_MODEL',
|
|
15
|
+
baseUrlEnvName: 'OPENAI_BASE_URL',
|
|
16
|
+
},
|
|
17
|
+
anthropic: {
|
|
18
|
+
envName: 'ANTHROPIC_API_KEY',
|
|
19
|
+
modelEnvName: 'ANTHROPIC_MODEL',
|
|
20
|
+
baseUrlEnvName: 'ANTHROPIC_BASE_URL',
|
|
21
|
+
},
|
|
22
|
+
gemini: {
|
|
23
|
+
envName: 'GEMINI_API_KEY',
|
|
24
|
+
modelEnvName: 'GEMINI_MODEL',
|
|
25
|
+
baseUrlEnvName: 'GEMINI_BASE_URL',
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
function isChatProvider(value) {
|
|
29
|
+
return typeof value === 'string' && CHAT_PROVIDERS.includes(value);
|
|
30
|
+
}
|
|
31
|
+
/** Infer the provider from the API-key formats developers already recognize, without adding a picker. */
|
|
32
|
+
function detectChatProvider(key) {
|
|
33
|
+
if (key.startsWith('sk-ant-'))
|
|
34
|
+
return 'anthropic';
|
|
35
|
+
if (key.startsWith('AIza'))
|
|
36
|
+
return 'gemini';
|
|
37
|
+
return 'openai';
|
|
38
|
+
}
|
|
10
39
|
/** Parse the `/widget?args=` query param (a JSON object) into tool-call arguments; `{}` on absent/invalid. */
|
|
11
40
|
function parseArgsParam(raw) {
|
|
12
41
|
if (!raw)
|
|
@@ -21,6 +50,18 @@ function parseArgsParam(raw) {
|
|
|
21
50
|
return {};
|
|
22
51
|
}
|
|
23
52
|
}
|
|
53
|
+
/** Parse a JSON request body only when its top-level value is an object. */
|
|
54
|
+
function parseJsonObject(raw) {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(raw.toString('utf8'));
|
|
57
|
+
return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
58
|
+
? parsed
|
|
59
|
+
: undefined;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
24
65
|
/** Cap a value for the inspector log so a huge tool result does not flood the SSE stream. */
|
|
25
66
|
function capForLog(value, max = 20000) {
|
|
26
67
|
if (value === undefined)
|
|
@@ -84,21 +125,64 @@ export async function startPreview(options) {
|
|
|
84
125
|
const log = [];
|
|
85
126
|
const subscribers = new Set();
|
|
86
127
|
const reloadSubscribers = new Set();
|
|
87
|
-
//
|
|
128
|
+
// Provider keys live ONLY in this server process (session) or the env, never in the browser. The
|
|
88
129
|
// /widget iframes are same-origin and unsandboxed, so anything the page holds (localStorage or a JS var)
|
|
89
130
|
// is readable by widget scripts and could be exfiltrated. The browser sets it via POST /chat/key and then
|
|
90
131
|
// drops it; the gate state comes from GET /chat/status. The key is never logged or sent back to the page.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
132
|
+
const sessionApiKeys = {};
|
|
133
|
+
const sessionModels = {};
|
|
134
|
+
let activeChatProvider;
|
|
135
|
+
function resolveApiKey(provider) {
|
|
136
|
+
return sessionApiKeys[provider] ?? process.env[CHAT_PROVIDER_CONFIG[provider].envName];
|
|
94
137
|
}
|
|
95
|
-
function apiKeySource() {
|
|
96
|
-
if (
|
|
138
|
+
function apiKeySource(provider) {
|
|
139
|
+
if (sessionApiKeys[provider])
|
|
97
140
|
return 'session';
|
|
98
|
-
if (process.env.
|
|
141
|
+
if (process.env[CHAT_PROVIDER_CONFIG[provider].envName])
|
|
99
142
|
return 'env';
|
|
100
143
|
return 'none';
|
|
101
144
|
}
|
|
145
|
+
function configuredModel(provider) {
|
|
146
|
+
const optionModel = provider === 'openai'
|
|
147
|
+
? options.openaiModel
|
|
148
|
+
: provider === 'anthropic'
|
|
149
|
+
? options.anthropicModel
|
|
150
|
+
: options.geminiModel;
|
|
151
|
+
return (sessionModels[provider] ??
|
|
152
|
+
optionModel ??
|
|
153
|
+
process.env[CHAT_PROVIDER_CONFIG[provider].modelEnvName] ??
|
|
154
|
+
DEFAULT_CHAT_MODELS[provider]);
|
|
155
|
+
}
|
|
156
|
+
function providerStatus(provider) {
|
|
157
|
+
return {
|
|
158
|
+
hasKey: Boolean(resolveApiKey(provider)),
|
|
159
|
+
source: apiKeySource(provider),
|
|
160
|
+
envName: CHAT_PROVIDER_CONFIG[provider].envName,
|
|
161
|
+
model: configuredModel(provider),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function configuredChatProviders() {
|
|
165
|
+
return CHAT_PROVIDERS.filter((provider) => providerStatus(provider).hasKey);
|
|
166
|
+
}
|
|
167
|
+
function resolveActiveChatProvider() {
|
|
168
|
+
if (activeChatProvider && providerStatus(activeChatProvider).hasKey) {
|
|
169
|
+
return activeChatProvider;
|
|
170
|
+
}
|
|
171
|
+
const configured = configuredChatProviders();
|
|
172
|
+
if (configured.length === 1)
|
|
173
|
+
return configured[0];
|
|
174
|
+
if (configured.length === 0)
|
|
175
|
+
return 'openai';
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
function ambiguousProviderError() {
|
|
179
|
+
if (resolveActiveChatProvider() !== undefined)
|
|
180
|
+
return undefined;
|
|
181
|
+
return {
|
|
182
|
+
code: 'ambiguous_provider',
|
|
183
|
+
message: 'Multiple provider keys are configured. Paste one API key to choose this chat session.',
|
|
184
|
+
};
|
|
185
|
+
}
|
|
102
186
|
function record(entry) {
|
|
103
187
|
log.push(entry);
|
|
104
188
|
if (log.length > 500)
|
|
@@ -198,16 +282,18 @@ export async function startPreview(options) {
|
|
|
198
282
|
});
|
|
199
283
|
}
|
|
200
284
|
/** Run one chat-playground turn against the local tools; the API key is read fresh and never logged. */
|
|
201
|
-
async function handleChat(incoming, apiKey) {
|
|
285
|
+
async function handleChat(provider, incoming, apiKey, model) {
|
|
202
286
|
const messages = incoming[0]?.role === 'system'
|
|
203
287
|
? incoming
|
|
204
288
|
: [{ role: 'system', content: CHAT_SYSTEM_PROMPT }, ...incoming];
|
|
205
|
-
|
|
289
|
+
const baseUrl = process.env[CHAT_PROVIDER_CONFIG[provider].baseUrlEnvName];
|
|
290
|
+
return runProviderChatTurn({
|
|
291
|
+
provider,
|
|
206
292
|
messages,
|
|
207
293
|
tools: await chatToolDefs(),
|
|
208
294
|
apiKey,
|
|
209
|
-
model:
|
|
210
|
-
...(
|
|
295
|
+
model: model?.trim() || configuredModel(provider),
|
|
296
|
+
...(baseUrl ? { baseUrl } : {}),
|
|
211
297
|
callTool: async (name, args) => {
|
|
212
298
|
const call = await forward({
|
|
213
299
|
method: 'tools/call',
|
|
@@ -225,7 +311,7 @@ export async function startPreview(options) {
|
|
|
225
311
|
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
226
312
|
if (req.method === 'GET' && url.pathname === '/') {
|
|
227
313
|
res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
|
228
|
-
res.end(harnessHtml(
|
|
314
|
+
res.end(harnessHtml(options));
|
|
229
315
|
return;
|
|
230
316
|
}
|
|
231
317
|
if (req.method === 'GET' && url.pathname === '/widget') {
|
|
@@ -266,9 +352,16 @@ export async function startPreview(options) {
|
|
|
266
352
|
return;
|
|
267
353
|
}
|
|
268
354
|
if (req.method === 'GET' && url.pathname === '/chat/status') {
|
|
269
|
-
// Report only whether
|
|
355
|
+
// Report only whether each key is available, plus non-secret configuration. Never return a key.
|
|
356
|
+
const activeProvider = resolveActiveChatProvider();
|
|
357
|
+
const error = ambiguousProviderError();
|
|
270
358
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
271
|
-
res.end(JSON.stringify({
|
|
359
|
+
res.end(JSON.stringify({
|
|
360
|
+
activeProvider: activeProvider ?? null,
|
|
361
|
+
requiresKey: Boolean(error),
|
|
362
|
+
error,
|
|
363
|
+
providers: Object.fromEntries(CHAT_PROVIDERS.map((provider) => [provider, providerStatus(provider)])),
|
|
364
|
+
}));
|
|
272
365
|
return;
|
|
273
366
|
}
|
|
274
367
|
if (req.method === 'POST' && url.pathname === '/chat/key') {
|
|
@@ -276,48 +369,101 @@ export async function startPreview(options) {
|
|
|
276
369
|
const chunks = [];
|
|
277
370
|
req.on('data', (c) => chunks.push(c));
|
|
278
371
|
req.on('end', () => {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
key = String(JSON.parse(Buffer.concat(chunks).toString('utf8')).key ?? '');
|
|
282
|
-
}
|
|
283
|
-
catch {
|
|
372
|
+
const body = parseJsonObject(Buffer.concat(chunks));
|
|
373
|
+
if (!body) {
|
|
284
374
|
res.writeHead(400, { 'content-type': 'application/json' });
|
|
285
375
|
res.end(JSON.stringify({ error: { code: 'invalid_json', message: 'invalid JSON' } }));
|
|
286
376
|
return;
|
|
287
377
|
}
|
|
288
|
-
const
|
|
289
|
-
|
|
378
|
+
const submittedKey = typeof body.key === 'string' ? body.key.trim() : '';
|
|
379
|
+
const provider = submittedKey === ''
|
|
380
|
+
? (body.provider ?? resolveActiveChatProvider())
|
|
381
|
+
: detectChatProvider(submittedKey);
|
|
382
|
+
const ambiguity = ambiguousProviderError();
|
|
383
|
+
if (submittedKey === '' && ambiguity) {
|
|
384
|
+
res.writeHead(409, { 'content-type': 'application/json' });
|
|
385
|
+
res.end(JSON.stringify({ error: ambiguity }));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (!isChatProvider(provider)) {
|
|
389
|
+
res.writeHead(400, { 'content-type': 'application/json' });
|
|
390
|
+
res.end(JSON.stringify({ error: { code: 'invalid_provider', message: 'invalid provider' } }));
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (body.key !== undefined) {
|
|
394
|
+
if (submittedKey === '')
|
|
395
|
+
delete sessionApiKeys[provider];
|
|
396
|
+
else
|
|
397
|
+
sessionApiKeys[provider] = submittedKey;
|
|
398
|
+
}
|
|
399
|
+
const model = typeof body.model === 'string' ? body.model.trim() : '';
|
|
400
|
+
if (model)
|
|
401
|
+
sessionModels[provider] = model;
|
|
402
|
+
if (resolveApiKey(provider))
|
|
403
|
+
activeChatProvider = provider;
|
|
404
|
+
else if (activeChatProvider === provider)
|
|
405
|
+
activeChatProvider = undefined;
|
|
290
406
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
291
|
-
res.end(JSON.stringify({ ok: true,
|
|
407
|
+
res.end(JSON.stringify({ ok: true, provider, ...providerStatus(provider) }));
|
|
292
408
|
});
|
|
293
409
|
return;
|
|
294
410
|
}
|
|
295
411
|
if (req.method === 'POST' && url.pathname === '/chat') {
|
|
296
|
-
// The key lives server-side (session key set via /chat/key, or the env). The browser never sends it.
|
|
297
|
-
const apiKey = resolveApiKey();
|
|
298
|
-
if (!apiKey) {
|
|
299
|
-
res.writeHead(200, { 'content-type': 'application/json' });
|
|
300
|
-
res.end(JSON.stringify({
|
|
301
|
-
error: {
|
|
302
|
-
code: 'no_api_key',
|
|
303
|
-
message: 'No OpenAI key set. Add one in the Chat tab or set OPENAI_API_KEY.',
|
|
304
|
-
},
|
|
305
|
-
}));
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
412
|
const chunks = [];
|
|
309
413
|
req.on('data', (c) => chunks.push(c));
|
|
310
414
|
req.on('end', () => {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
body = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
314
|
-
}
|
|
315
|
-
catch {
|
|
415
|
+
const body = parseJsonObject(Buffer.concat(chunks));
|
|
416
|
+
if (!body) {
|
|
316
417
|
res.writeHead(400, { 'content-type': 'application/json' });
|
|
317
418
|
res.end(JSON.stringify({ error: { code: 'invalid_json', message: 'invalid JSON' } }));
|
|
318
419
|
return;
|
|
319
420
|
}
|
|
320
|
-
|
|
421
|
+
const requestedProvider = body.provider;
|
|
422
|
+
if (requestedProvider !== undefined && !isChatProvider(requestedProvider)) {
|
|
423
|
+
res.writeHead(400, { 'content-type': 'application/json' });
|
|
424
|
+
res.end(JSON.stringify({ error: { code: 'invalid_provider', message: 'invalid provider' } }));
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const ambiguity = ambiguousProviderError();
|
|
428
|
+
if (ambiguity) {
|
|
429
|
+
res.writeHead(409, { 'content-type': 'application/json' });
|
|
430
|
+
res.end(JSON.stringify({ error: ambiguity }));
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const provider = resolveActiveChatProvider();
|
|
434
|
+
if (!provider) {
|
|
435
|
+
res.writeHead(409, { 'content-type': 'application/json' });
|
|
436
|
+
res.end(JSON.stringify({
|
|
437
|
+
error: {
|
|
438
|
+
code: 'ambiguous_provider',
|
|
439
|
+
message: 'Paste one API key to choose this chat session.',
|
|
440
|
+
},
|
|
441
|
+
}));
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (requestedProvider !== undefined && requestedProvider !== provider) {
|
|
445
|
+
res.writeHead(409, { 'content-type': 'application/json' });
|
|
446
|
+
res.end(JSON.stringify({
|
|
447
|
+
error: {
|
|
448
|
+
code: 'provider_mismatch',
|
|
449
|
+
message: 'The chat provider does not match the connected API key.',
|
|
450
|
+
},
|
|
451
|
+
}));
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
// The key and inferred provider live server-side. The browser can only use the bound provider.
|
|
455
|
+
const apiKey = resolveApiKey(provider);
|
|
456
|
+
if (!apiKey) {
|
|
457
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
458
|
+
res.end(JSON.stringify({
|
|
459
|
+
error: {
|
|
460
|
+
code: 'no_api_key',
|
|
461
|
+
message: `No key set for this provider. Add one in Chat or set ${CHAT_PROVIDER_CONFIG[provider].envName}.`,
|
|
462
|
+
},
|
|
463
|
+
}));
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
handleChat(provider, Array.isArray(body.messages) ? body.messages : [], apiKey, typeof body.model === 'string' ? body.model : undefined)
|
|
321
467
|
.then((result) => {
|
|
322
468
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
323
469
|
res.end(JSON.stringify(result));
|