@tyvm/knowhow 0.0.110 → 0.0.112
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/package.json +1 -1
- package/scripts/test-repetition-hint.ts +234 -0
- package/src/auth/browserLogin.ts +129 -3
- package/src/chat/CliChatService.ts +14 -6
- package/src/chat/modules/AgentModule.ts +4 -1
- package/src/chat/modules/ClipboardImageModule.ts +136 -0
- package/src/chat/modules/InternalChatModule.ts +3 -0
- package/src/chat/modules/RendererModule.ts +30 -2
- package/src/clients/xai.ts +20 -3
- package/src/login.ts +3 -2
- package/src/processors/CustomVariables.ts +175 -0
- package/src/services/EventService.ts +5 -33
- package/src/services/Mcp.ts +14 -1
- package/src/utils/http.ts +9 -2
- package/src/utils/index.ts +1 -0
- package/tests/fixtures/fake-secret.txt +1 -0
- package/tests/manual/modalities/xai.modalities.test.ts +1 -1
- package/tests/processors/CustomVariables.test.ts +416 -1
- package/ts_build/package.json +1 -1
- package/ts_build/src/auth/browserLogin.d.ts +2 -0
- package/ts_build/src/auth/browserLogin.js +91 -3
- package/ts_build/src/auth/browserLogin.js.map +1 -1
- package/ts_build/src/chat/CliChatService.js +9 -4
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.js +3 -1
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/ClipboardImageModule.d.ts +15 -0
- package/ts_build/src/chat/modules/ClipboardImageModule.js +157 -0
- package/ts_build/src/chat/modules/ClipboardImageModule.js.map +1 -0
- package/ts_build/src/chat/modules/InternalChatModule.d.ts +1 -0
- package/ts_build/src/chat/modules/InternalChatModule.js +3 -0
- package/ts_build/src/chat/modules/InternalChatModule.js.map +1 -1
- package/ts_build/src/chat/modules/RendererModule.js +30 -1
- package/ts_build/src/chat/modules/RendererModule.js.map +1 -1
- package/ts_build/src/clients/xai.js +14 -2
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/login.js +2 -2
- package/ts_build/src/login.js.map +1 -1
- package/ts_build/src/processors/CustomVariables.d.ts +10 -0
- package/ts_build/src/processors/CustomVariables.js +127 -0
- package/ts_build/src/processors/CustomVariables.js.map +1 -1
- package/ts_build/src/services/EventService.d.ts +0 -4
- package/ts_build/src/services/EventService.js +4 -15
- package/ts_build/src/services/EventService.js.map +1 -1
- package/ts_build/src/services/Mcp.js +9 -1
- package/ts_build/src/services/Mcp.js.map +1 -1
- package/ts_build/src/utils/http.d.ts +2 -1
- package/ts_build/src/utils/http.js +11 -2
- package/ts_build/src/utils/http.js.map +1 -1
- package/ts_build/src/utils/index.js.map +1 -1
- package/ts_build/tests/manual/modalities/xai.modalities.test.js +1 -1
- package/ts_build/tests/manual/modalities/xai.modalities.test.js.map +1 -1
- package/ts_build/tests/processors/CustomVariables.test.js +347 -0
- package/ts_build/tests/processors/CustomVariables.test.js.map +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Message } from "../../src/clients/types";
|
|
1
|
+
import { Message } from "../../src/clients/types";
|
|
2
2
|
import { CustomVariables } from "../../src/processors/CustomVariables";
|
|
3
3
|
import { ToolsService } from "../../src/services";
|
|
4
|
+
import * as fs from "fs";
|
|
4
5
|
|
|
5
6
|
describe("CustomVariables", () => {
|
|
6
7
|
let customVariables: CustomVariables;
|
|
@@ -553,4 +554,418 @@ describe("CustomVariables", () => {
|
|
|
553
554
|
expect(modifiedMessages[0].content).toBe("Empty: '' Spaces: ' '");
|
|
554
555
|
});
|
|
555
556
|
});
|
|
557
|
+
|
|
558
|
+
describe("JWT token use case", () => {
|
|
559
|
+
let setVariableFunction: (name: string, contents: any) => string;
|
|
560
|
+
|
|
561
|
+
const LONG_JWT =
|
|
562
|
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
|
|
563
|
+
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3MTYyMzkwMjIsInJvbGVzIjpbImFkbWluIiwidXNlciJdLCJvcmciOiJhY21lLWNvcnAiLCJlbWFpbCI6ImpvaG5AYWNtZS5jb20ifQ." +
|
|
564
|
+
"SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
565
|
+
|
|
566
|
+
beforeEach(() => {
|
|
567
|
+
const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
|
|
568
|
+
const setVariableCall = addFunctionsCalls.find(
|
|
569
|
+
(call) => call[0].setVariable
|
|
570
|
+
);
|
|
571
|
+
setVariableFunction = setVariableCall[0].setVariable;
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it("should store a long JWT and substitute it in a tool call argument", async () => {
|
|
575
|
+
// LLM stores the JWT once
|
|
576
|
+
setVariableFunction("jwt_token", LONG_JWT);
|
|
577
|
+
|
|
578
|
+
// LLM uses {{jwt_token}} in a tool call instead of repeating the full JWT
|
|
579
|
+
const messages: Message[] = [
|
|
580
|
+
{
|
|
581
|
+
role: "assistant",
|
|
582
|
+
content: null,
|
|
583
|
+
tool_calls: [
|
|
584
|
+
{
|
|
585
|
+
id: "call_abc",
|
|
586
|
+
type: "function",
|
|
587
|
+
function: {
|
|
588
|
+
name: "execCommand",
|
|
589
|
+
arguments: JSON.stringify({
|
|
590
|
+
command: 'curl -H "Authorization: Bearer {{jwt_token}}" https://api.example.com/data',
|
|
591
|
+
}),
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
],
|
|
595
|
+
},
|
|
596
|
+
];
|
|
597
|
+
|
|
598
|
+
const processor = customVariables.createProcessor();
|
|
599
|
+
const modifiedMessages = [...messages];
|
|
600
|
+
await processor(messages, modifiedMessages);
|
|
601
|
+
|
|
602
|
+
// The tool call argument should have the full JWT substituted in
|
|
603
|
+
const args = JSON.parse(modifiedMessages[0].tool_calls![0].function.arguments);
|
|
604
|
+
expect(args.command).toBe(
|
|
605
|
+
`curl -H "Authorization: Bearer ${LONG_JWT}" https://api.example.com/data`
|
|
606
|
+
);
|
|
607
|
+
// And the original message should be unchanged
|
|
608
|
+
expect(messages[0].tool_calls![0].function.arguments).toContain("{{jwt_token}}");
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it("should allow reusing the JWT variable across multiple tool calls without repeating it", async () => {
|
|
612
|
+
setVariableFunction("jwt_token", LONG_JWT);
|
|
613
|
+
|
|
614
|
+
// Simulate multiple tool calls all using {{jwt_token}} - LLM never outputs full JWT again
|
|
615
|
+
const toolCallMessages: Message[] = [
|
|
616
|
+
{
|
|
617
|
+
role: "assistant",
|
|
618
|
+
content: null,
|
|
619
|
+
tool_calls: [
|
|
620
|
+
{
|
|
621
|
+
id: "call_1",
|
|
622
|
+
type: "function",
|
|
623
|
+
function: {
|
|
624
|
+
name: "execCommand",
|
|
625
|
+
arguments: JSON.stringify({ command: 'curl -H "Authorization: Bearer {{jwt_token}}" https://api.example.com/users' }),
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
],
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
role: "assistant",
|
|
632
|
+
content: null,
|
|
633
|
+
tool_calls: [
|
|
634
|
+
{
|
|
635
|
+
id: "call_2",
|
|
636
|
+
type: "function",
|
|
637
|
+
function: {
|
|
638
|
+
name: "execCommand",
|
|
639
|
+
arguments: JSON.stringify({ command: 'curl -H "Authorization: Bearer {{jwt_token}}" https://api.example.com/posts' }),
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
],
|
|
643
|
+
},
|
|
644
|
+
];
|
|
645
|
+
|
|
646
|
+
const processor = customVariables.createProcessor();
|
|
647
|
+
const modifiedMessages = [...toolCallMessages];
|
|
648
|
+
await processor(toolCallMessages, modifiedMessages);
|
|
649
|
+
|
|
650
|
+
// Both tool calls should have the full JWT
|
|
651
|
+
const args1 = JSON.parse(modifiedMessages[0].tool_calls![0].function.arguments);
|
|
652
|
+
const args2 = JSON.parse(modifiedMessages[1].tool_calls![0].function.arguments);
|
|
653
|
+
|
|
654
|
+
expect(args1.command).toContain(LONG_JWT);
|
|
655
|
+
expect(args2.command).toContain(LONG_JWT);
|
|
656
|
+
|
|
657
|
+
// Original messages should still have the placeholder
|
|
658
|
+
expect(toolCallMessages[0].tool_calls![0].function.arguments).toContain("{{jwt_token}}");
|
|
659
|
+
expect(toolCallMessages[1].tool_calls![0].function.arguments).toContain("{{jwt_token}}");
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
describe("storeToolCallToVariable privacy proof", () => {
|
|
663
|
+
/**
|
|
664
|
+
* This test proves that when an LLM uses storeToolCallToVariable to load
|
|
665
|
+
* a sensitive value (e.g. a JWT from a file), the LLM *never* sees the
|
|
666
|
+
* actual value in any message. The tool response only says
|
|
667
|
+
* "stored in variable X" - the raw secret stays server-side in the
|
|
668
|
+
* variable storage, and the LLM uses {{jwt_token}} as a placeholder.
|
|
669
|
+
*/
|
|
670
|
+
let storeToolCallFunction: (
|
|
671
|
+
varName: string,
|
|
672
|
+
toolName: string,
|
|
673
|
+
toolArgs: string
|
|
674
|
+
) => Promise<string>;
|
|
675
|
+
let setVariableFunction: (name: string, contents: any) => string;
|
|
676
|
+
|
|
677
|
+
beforeEach(() => {
|
|
678
|
+
const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
|
|
679
|
+
storeToolCallFunction = addFunctionsCalls.find(
|
|
680
|
+
(call) => call[0].storeToolCallToVariable
|
|
681
|
+
)![0].storeToolCallToVariable;
|
|
682
|
+
setVariableFunction = addFunctionsCalls.find(
|
|
683
|
+
(call) => call[0].setVariable
|
|
684
|
+
)![0].setVariable;
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
it("should store JWT from a file without the LLM ever seeing the value", async () => {
|
|
688
|
+
// Simulate a tool that reads the JWT file (like execCommand cat .knowhow/.jwt)
|
|
689
|
+
const jwtContent = fs.readFileSync(`${__dirname}/../fixtures/fake-secret.txt`, "utf-8").trim();
|
|
690
|
+
|
|
691
|
+
// The tool returns the file contents - but only to storeToolCallToVariable, not to the LLM
|
|
692
|
+
mockToolsService.callTool.mockResolvedValue(jwtContent as any);
|
|
693
|
+
|
|
694
|
+
// LLM calls: storeToolCallToVariable("jwt_token", "execCommand", '{"command":"cat .knowhow/.jwt"}')
|
|
695
|
+
const toolResponse = await storeToolCallFunction(
|
|
696
|
+
"jwt_token",
|
|
697
|
+
"execCommand",
|
|
698
|
+
JSON.stringify({ command: "cat tests/fixtures/fake-secret.txt" })
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
// The LLM only sees this confirmation message - NOT the JWT value itself
|
|
702
|
+
expect(toolResponse).toBe(
|
|
703
|
+
'Tool call result for "execCommand" has been stored in variable "jwt_token".'
|
|
704
|
+
);
|
|
705
|
+
expect(toolResponse).not.toContain(jwtContent);
|
|
706
|
+
expect(toolResponse).not.toContain("eyJ"); // No JWT content in the response
|
|
707
|
+
|
|
708
|
+
// The JWT IS stored internally and can be substituted into future tool calls
|
|
709
|
+
const processor = customVariables.createProcessor();
|
|
710
|
+
|
|
711
|
+
const messages: Message[] = [
|
|
712
|
+
{
|
|
713
|
+
role: "assistant",
|
|
714
|
+
content: null,
|
|
715
|
+
tool_calls: [
|
|
716
|
+
{
|
|
717
|
+
id: "call_curl",
|
|
718
|
+
type: "function",
|
|
719
|
+
function: {
|
|
720
|
+
name: "execCommand",
|
|
721
|
+
// LLM uses placeholder - never outputs the actual JWT
|
|
722
|
+
arguments: JSON.stringify({
|
|
723
|
+
command: 'curl -H "Authorization: Bearer {{jwt_token}}" https://api.example.com',
|
|
724
|
+
}),
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
],
|
|
728
|
+
},
|
|
729
|
+
];
|
|
730
|
+
|
|
731
|
+
const modifiedMessages = [...messages];
|
|
732
|
+
await processor(messages, modifiedMessages);
|
|
733
|
+
|
|
734
|
+
// The actual JWT is injected at execution time
|
|
735
|
+
const args = JSON.parse(modifiedMessages[0].tool_calls![0].function.arguments);
|
|
736
|
+
expect(args.command).toContain(jwtContent);
|
|
737
|
+
|
|
738
|
+
// But the original message still has the placeholder (LLM never saw the JWT)
|
|
739
|
+
expect(messages[0].tool_calls![0].function.arguments).toContain("{{jwt_token}}");
|
|
740
|
+
expect(messages[0].tool_calls![0].function.arguments).not.toContain(jwtContent);
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
it("should prove the full storeToolCallToVariable flow with real JWT file", async () => {
|
|
744
|
+
// Use a fake secret file instead of a real JWT, to prove the secret is never exposed
|
|
745
|
+
const fakePath = `${__dirname}/../fixtures/fake-secret.txt`;
|
|
746
|
+
const fakeSecret = fs.readFileSync(fakePath, "utf-8").trim();
|
|
747
|
+
|
|
748
|
+
// Simulate the execCommand tool returning the JWT file contents
|
|
749
|
+
mockToolsService.callTool.mockResolvedValue(fakeSecret as any);
|
|
750
|
+
|
|
751
|
+
// Step 1: LLM stores JWT via storeToolCallToVariable - only gets a confirmation back
|
|
752
|
+
const storeResponse = await storeToolCallFunction(
|
|
753
|
+
"jwt_token",
|
|
754
|
+
"execCommand",
|
|
755
|
+
JSON.stringify({ command: `cat ${fakePath}` })
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
// LLM message history only contains this - not the JWT
|
|
759
|
+
expect(storeResponse).toContain('stored in variable "jwt_token"');
|
|
760
|
+
expect(storeResponse).not.toContain(fakeSecret);
|
|
761
|
+
|
|
762
|
+
// Step 2: LLM uses {{jwt_token}} in subsequent curl calls
|
|
763
|
+
const processor = customVariables.createProcessor();
|
|
764
|
+
const curlMessages: Message[] = [
|
|
765
|
+
{
|
|
766
|
+
role: "assistant",
|
|
767
|
+
content: null,
|
|
768
|
+
tool_calls: [
|
|
769
|
+
{
|
|
770
|
+
id: "call_api1",
|
|
771
|
+
type: "function",
|
|
772
|
+
function: {
|
|
773
|
+
name: "execCommand",
|
|
774
|
+
arguments: JSON.stringify({
|
|
775
|
+
command: 'curl -H "Authorization: Bearer {{jwt_token}}" https://api.example.com/users',
|
|
776
|
+
}),
|
|
777
|
+
},
|
|
778
|
+
},
|
|
779
|
+
],
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
role: "assistant",
|
|
783
|
+
content: null,
|
|
784
|
+
tool_calls: [
|
|
785
|
+
{
|
|
786
|
+
id: "call_api2",
|
|
787
|
+
type: "function",
|
|
788
|
+
function: {
|
|
789
|
+
name: "execCommand",
|
|
790
|
+
arguments: JSON.stringify({
|
|
791
|
+
command: 'curl -H "Authorization: Bearer {{jwt_token}}" https://api.example.com/posts',
|
|
792
|
+
}),
|
|
793
|
+
},
|
|
794
|
+
},
|
|
795
|
+
],
|
|
796
|
+
},
|
|
797
|
+
];
|
|
798
|
+
|
|
799
|
+
const modifiedMessages = [...curlMessages];
|
|
800
|
+
await processor(curlMessages, modifiedMessages);
|
|
801
|
+
|
|
802
|
+
// Both calls get the real JWT injected
|
|
803
|
+
const args1 = JSON.parse(modifiedMessages[0].tool_calls![0].function.arguments);
|
|
804
|
+
const args2 = JSON.parse(modifiedMessages[1].tool_calls![0].function.arguments);
|
|
805
|
+
expect(args1.command).toContain(fakeSecret);
|
|
806
|
+
expect(args2.command).toContain(fakeSecret);
|
|
807
|
+
// Original messages still have placeholder - JWT never appeared in LLM messages
|
|
808
|
+
expect(curlMessages[0].tool_calls![0].function.arguments).not.toContain(fakeSecret);
|
|
809
|
+
expect(curlMessages[1].tool_calls![0].function.arguments).not.toContain(fakeSecret);
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
describe("createRepetitionHintProcessor", () => {
|
|
814
|
+
it("should append a hint when a large string is repeated across multiple tool calls", async () => {
|
|
815
|
+
const longString = "x".repeat(100);
|
|
816
|
+
const messages: Message[] = [
|
|
817
|
+
{
|
|
818
|
+
role: "assistant",
|
|
819
|
+
content: null,
|
|
820
|
+
tool_calls: [{ id: "c1", type: "function", function: { name: "toolA", arguments: JSON.stringify({ token: longString }) } }],
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
role: "assistant",
|
|
824
|
+
content: null,
|
|
825
|
+
tool_calls: [{ id: "c2", type: "function", function: { name: "toolA", arguments: JSON.stringify({ token: longString }) } }],
|
|
826
|
+
},
|
|
827
|
+
];
|
|
828
|
+
|
|
829
|
+
const processor = customVariables.createRepetitionHintProcessor({ minLength: 50, minRepetitions: 2 });
|
|
830
|
+
const modified = JSON.parse(JSON.stringify(messages));
|
|
831
|
+
await processor(messages, modified);
|
|
832
|
+
|
|
833
|
+
// A hint message should be appended
|
|
834
|
+
const hint = modified[modified.length - 1];
|
|
835
|
+
expect(hint.role).toBe("user");
|
|
836
|
+
expect(hint.content).toContain("large repetitions");
|
|
837
|
+
expect(hint.content).toContain("toolA");
|
|
838
|
+
expect(hint.content).toContain("setVariable");
|
|
839
|
+
expect(hint.content).toContain("storeToolCallToVariable");
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
it("should not append a hint when strings are short", async () => {
|
|
843
|
+
const messages: Message[] = [
|
|
844
|
+
{
|
|
845
|
+
role: "assistant",
|
|
846
|
+
content: null,
|
|
847
|
+
tool_calls: [{ id: "c1", type: "function", function: { name: "toolA", arguments: JSON.stringify({ token: "short" }) } }],
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
role: "assistant",
|
|
851
|
+
content: null,
|
|
852
|
+
tool_calls: [{ id: "c2", type: "function", function: { name: "toolA", arguments: JSON.stringify({ token: "short" }) } }],
|
|
853
|
+
},
|
|
854
|
+
];
|
|
855
|
+
|
|
856
|
+
const processor = customVariables.createRepetitionHintProcessor({ minLength: 50, minRepetitions: 2 });
|
|
857
|
+
const modified = JSON.parse(JSON.stringify(messages));
|
|
858
|
+
await processor(messages, modified);
|
|
859
|
+
|
|
860
|
+
// No hint should be appended - length of messages unchanged
|
|
861
|
+
expect(modified.length).toBe(messages.length);
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
it("should not append a hint when a large string appears only once", async () => {
|
|
865
|
+
const longString = "y".repeat(100);
|
|
866
|
+
const messages: Message[] = [
|
|
867
|
+
{
|
|
868
|
+
role: "assistant",
|
|
869
|
+
content: null,
|
|
870
|
+
tool_calls: [{ id: "c1", type: "function", function: { name: "toolB", arguments: JSON.stringify({ value: longString }) } }],
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
role: "assistant",
|
|
874
|
+
content: null,
|
|
875
|
+
tool_calls: [{ id: "c2", type: "function", function: { name: "toolB", arguments: JSON.stringify({ value: "something_different_entirely" }) } }],
|
|
876
|
+
},
|
|
877
|
+
];
|
|
878
|
+
|
|
879
|
+
const processor = customVariables.createRepetitionHintProcessor({ minLength: 50, minRepetitions: 2 });
|
|
880
|
+
const modified = JSON.parse(JSON.stringify(messages));
|
|
881
|
+
await processor(messages, modified);
|
|
882
|
+
expect(modified.length).toBe(messages.length);
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
it("should list all tool names that use repeated values", async () => {
|
|
886
|
+
const longString = "z".repeat(100);
|
|
887
|
+
const messages: Message[] = [
|
|
888
|
+
{
|
|
889
|
+
role: "assistant",
|
|
890
|
+
content: null,
|
|
891
|
+
tool_calls: [{ id: "c1", type: "function", function: { name: "toolX", arguments: JSON.stringify({ auth: longString }) } }],
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
role: "assistant",
|
|
895
|
+
content: null,
|
|
896
|
+
tool_calls: [{ id: "c2", type: "function", function: { name: "toolY", arguments: JSON.stringify({ auth: longString }) } }],
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
role: "assistant",
|
|
900
|
+
content: null,
|
|
901
|
+
tool_calls: [{ id: "c3", type: "function", function: { name: "toolZ", arguments: JSON.stringify({ auth: longString }) } }],
|
|
902
|
+
},
|
|
903
|
+
];
|
|
904
|
+
const processor = customVariables.createRepetitionHintProcessor({ minLength: 50, minRepetitions: 2 });
|
|
905
|
+
const modified = JSON.parse(JSON.stringify(messages));
|
|
906
|
+
await processor(messages, modified);
|
|
907
|
+
const hint = modified[modified.length - 1];
|
|
908
|
+
expect(hint.content).toContain("toolX");
|
|
909
|
+
expect(hint.content).toContain("toolY");
|
|
910
|
+
expect(hint.content).toContain("toolZ");
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
it("should use default options when none provided", async () => {
|
|
914
|
+
const longString = "a".repeat(60); // > 50 default minLength
|
|
915
|
+
const messages: Message[] = [
|
|
916
|
+
{
|
|
917
|
+
role: "assistant",
|
|
918
|
+
content: null,
|
|
919
|
+
tool_calls: [{ id: "c1", type: "function", function: { name: "myTool", arguments: JSON.stringify({ key: longString }) } }],
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
role: "assistant",
|
|
923
|
+
content: null,
|
|
924
|
+
tool_calls: [{ id: "c2", type: "function", function: { name: "myTool", arguments: JSON.stringify({ key: longString }) } }],
|
|
925
|
+
},
|
|
926
|
+
];
|
|
927
|
+
const processor = customVariables.createRepetitionHintProcessor(); // default options
|
|
928
|
+
const modified = JSON.parse(JSON.stringify(messages));
|
|
929
|
+
await processor(messages, modified);
|
|
930
|
+
const hint = modified[modified.length - 1];
|
|
931
|
+
expect(hint.role).toBe("user");
|
|
932
|
+
expect(hint.content).toContain("myTool");
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
it("should detect repeated substrings embedded within different larger strings", async () => {
|
|
936
|
+
// Simulate the JWT-in-curl-command pattern:
|
|
937
|
+
// Each command is unique but all contain the same JWT substring
|
|
938
|
+
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJmYWtlLXVzZXItaWQifQ.FAKE_SIGNATURE_DO_NOT_USE";
|
|
939
|
+
const messages: Message[] = [
|
|
940
|
+
{
|
|
941
|
+
role: "assistant",
|
|
942
|
+
content: null,
|
|
943
|
+
tool_calls: [{
|
|
944
|
+
id: "c1", type: "function",
|
|
945
|
+
function: { name: "execCommand", arguments: JSON.stringify({ command: `curl -H 'Authorization: Bearer ${jwt}' https://api.example.com/endpoint-one` }) }
|
|
946
|
+
}],
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
role: "assistant",
|
|
950
|
+
content: null,
|
|
951
|
+
tool_calls: [{
|
|
952
|
+
id: "c2", type: "function",
|
|
953
|
+
function: { name: "execCommand", arguments: JSON.stringify({ command: `curl -H 'Authorization: Bearer ${jwt}' https://api.example.com/endpoint-two --data '{"key":"value"}'` }) }
|
|
954
|
+
}],
|
|
955
|
+
},
|
|
956
|
+
];
|
|
957
|
+
|
|
958
|
+
const processor = customVariables.createRepetitionHintProcessor({ minLength: 50, minRepetitions: 2, minSubstringLength: 50 });
|
|
959
|
+
const modified = JSON.parse(JSON.stringify(messages));
|
|
960
|
+
await processor(messages, modified);
|
|
961
|
+
|
|
962
|
+
// Should have detected the JWT appearing in both commands and added a hint
|
|
963
|
+
expect(modified.length).toBe(3);
|
|
964
|
+
const hint = modified[modified.length - 1];
|
|
965
|
+
expect(hint.role).toBe("user");
|
|
966
|
+
expect(hint.content).toContain("execCommand");
|
|
967
|
+
expect(hint.content).toContain("setVariable");
|
|
968
|
+
});
|
|
969
|
+
});
|
|
970
|
+
});
|
|
556
971
|
});
|
package/ts_build/package.json
CHANGED
|
@@ -6,7 +6,9 @@ export declare class BrowserLoginService {
|
|
|
6
6
|
private createSession;
|
|
7
7
|
private storeJwt;
|
|
8
8
|
private sleep;
|
|
9
|
+
private waitForDeviceConfirmation;
|
|
9
10
|
private setupSignalHandlers;
|
|
10
11
|
}
|
|
12
|
+
export declare function getCliUserAgent(): string;
|
|
11
13
|
export declare function openBrowser(url: string): Promise<void>;
|
|
12
14
|
export declare function validateJwt(jwt: string): boolean;
|
|
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.BrowserLoginService = void 0;
|
|
40
|
+
exports.getCliUserAgent = getCliUserAgent;
|
|
40
41
|
exports.openBrowser = openBrowser;
|
|
41
42
|
exports.validateJwt = validateJwt;
|
|
42
43
|
const http_1 = __importDefault(require("../utils/http"));
|
|
@@ -44,6 +45,7 @@ const child_process_1 = require("child_process");
|
|
|
44
45
|
const util_1 = require("util");
|
|
45
46
|
const os = __importStar(require("os"));
|
|
46
47
|
const fs = __importStar(require("fs"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
47
49
|
const KnowhowClient_1 = require("../services/KnowhowClient");
|
|
48
50
|
const spinner_1 = require("./spinner");
|
|
49
51
|
const errors_1 = require("./errors");
|
|
@@ -94,9 +96,25 @@ class BrowserLoginService {
|
|
|
94
96
|
spinner.stop();
|
|
95
97
|
spinner.start("Authentication successful! Retrieving token");
|
|
96
98
|
const tokenResponse = await http_1.default.post(`${this.baseUrl}/api/cli-login/session/${sessionData.sessionId}/token`);
|
|
97
|
-
const
|
|
98
|
-
await this.storeJwt(jwt);
|
|
99
|
+
const tokenData = tokenResponse.data;
|
|
99
100
|
spinner.stop();
|
|
101
|
+
if (tokenData.requiresDeviceConfirmation) {
|
|
102
|
+
if (tokenData.jwt) {
|
|
103
|
+
await this.storeJwt(tokenData.jwt);
|
|
104
|
+
}
|
|
105
|
+
console.log("\n⚠️ New device detected — device confirmation required!");
|
|
106
|
+
console.log("─────────────────────────────────────────────────────");
|
|
107
|
+
console.log("A confirmation code has been sent to your email.");
|
|
108
|
+
console.log("You must confirm this device in your browser to complete login.");
|
|
109
|
+
console.log("\nPlease check the browser window you just used to approve the CLI session.");
|
|
110
|
+
console.log("Enter the email code there to confirm this device.");
|
|
111
|
+
console.log("\nAlternatively, visit your settings page:");
|
|
112
|
+
console.log(` ${process.env.KNOWHOW_FRONTEND_URL || "https://knowhow.tyvm.ai"}/settings?tab=security`);
|
|
113
|
+
console.log("─────────────────────────────────────────────────────\n");
|
|
114
|
+
await this.waitForDeviceConfirmation(tokenData.jwt);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
await this.storeJwt(tokenData.jwt);
|
|
100
118
|
return;
|
|
101
119
|
}
|
|
102
120
|
else if (status.status.toLowerCase() === "denied") {
|
|
@@ -123,7 +141,7 @@ class BrowserLoginService {
|
|
|
123
141
|
}
|
|
124
142
|
async createSession() {
|
|
125
143
|
try {
|
|
126
|
-
const response = await http_1.default.post(`${this.baseUrl}/api/cli-login/session`, {});
|
|
144
|
+
const response = await http_1.default.post(`${this.baseUrl}/api/cli-login/session`, {}, { headers: { "User-Agent": getCliUserAgent() } });
|
|
127
145
|
return response.data;
|
|
128
146
|
}
|
|
129
147
|
catch (error) {
|
|
@@ -145,6 +163,61 @@ class BrowserLoginService {
|
|
|
145
163
|
async sleep(ms) {
|
|
146
164
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
147
165
|
}
|
|
166
|
+
async waitForDeviceConfirmation(jwt) {
|
|
167
|
+
const spinner = new spinner_1.Spinner();
|
|
168
|
+
spinner.start("Waiting for device confirmation");
|
|
169
|
+
let isCancelled = false;
|
|
170
|
+
const cancelHandler = () => {
|
|
171
|
+
isCancelled = true;
|
|
172
|
+
spinner.stop();
|
|
173
|
+
console.log("\n\nCancelled. Your token is stored — once you confirm the device, re-run your command.");
|
|
174
|
+
process.exit(0);
|
|
175
|
+
};
|
|
176
|
+
process.once("SIGINT", cancelHandler);
|
|
177
|
+
const maxAttempts = 120;
|
|
178
|
+
let attempt = 0;
|
|
179
|
+
while (attempt < maxAttempts) {
|
|
180
|
+
attempt++;
|
|
181
|
+
for (let i = 0; i < 10; i++) {
|
|
182
|
+
await this.sleep(500);
|
|
183
|
+
if (isCancelled)
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const response = await http_1.default.get(`${this.baseUrl}/api/users/me`, {
|
|
188
|
+
headers: { Authorization: `Bearer ${jwt}` },
|
|
189
|
+
timeout: 10000,
|
|
190
|
+
});
|
|
191
|
+
if (response.status === 200) {
|
|
192
|
+
spinner.stop();
|
|
193
|
+
process.removeListener("SIGINT", cancelHandler);
|
|
194
|
+
console.log("✅ Device confirmed! You are now logged in.");
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
if (http_1.default.isHttpError(error)) {
|
|
200
|
+
if (error.status === 403) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (error.status === 401) {
|
|
204
|
+
if (attempt >= 10) {
|
|
205
|
+
spinner.stop();
|
|
206
|
+
process.removeListener("SIGINT", cancelHandler);
|
|
207
|
+
throw new errors_1.BrowserLoginError("Token expired or revoked during device confirmation. Please run 'knowhow login' again.", "TOKEN_EXPIRED");
|
|
208
|
+
}
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
spinner.stop();
|
|
215
|
+
process.removeListener("SIGINT", cancelHandler);
|
|
216
|
+
console.log("\n⏰ Timed out waiting for device confirmation.");
|
|
217
|
+
console.log("Your token is stored — once you confirm the device at:");
|
|
218
|
+
console.log(` ${process.env.KNOWHOW_FRONTEND_URL || "https://knowhow.tyvm.ai"}/settings?tab=security`);
|
|
219
|
+
console.log("you can re-run your command and it will work.\n");
|
|
220
|
+
}
|
|
148
221
|
setupSignalHandlers() {
|
|
149
222
|
const gracefulShutdown = () => {
|
|
150
223
|
console.log("\n\nAuthentication cancelled by user.");
|
|
@@ -154,6 +227,21 @@ class BrowserLoginService {
|
|
|
154
227
|
}
|
|
155
228
|
}
|
|
156
229
|
exports.BrowserLoginService = BrowserLoginService;
|
|
230
|
+
function getCliUserAgent() {
|
|
231
|
+
let cliVersion = "unknown";
|
|
232
|
+
try {
|
|
233
|
+
const pkgPath = path.resolve(__dirname, "../../../package.json");
|
|
234
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
235
|
+
cliVersion = pkg.version ?? "unknown";
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
}
|
|
239
|
+
const platform = os.platform();
|
|
240
|
+
const osName = platform === "darwin" ? "macOS" :
|
|
241
|
+
platform === "win32" ? "Windows" :
|
|
242
|
+
platform === "linux" ? "Linux" : platform;
|
|
243
|
+
return `Knowhow CLI/${cliVersion} (${osName})`;
|
|
244
|
+
}
|
|
157
245
|
async function openBrowser(url) {
|
|
158
246
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
159
247
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browserLogin.js","sourceRoot":"","sources":["../../../src/auth/browserLogin.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"browserLogin.js","sourceRoot":"","sources":["../../../src/auth/browserLogin.ts"],"names":[],"mappings":"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"}
|
|
@@ -153,10 +153,15 @@ class CliChatService {
|
|
|
153
153
|
}
|
|
154
154
|
else {
|
|
155
155
|
const availableCommands = this.getCommandsForActiveModes();
|
|
156
|
-
|
|
157
|
-
.
|
|
158
|
-
.
|
|
159
|
-
|
|
156
|
+
const looksLikeFilepath = commandName.includes("/") ||
|
|
157
|
+
commandName.includes(".") ||
|
|
158
|
+
commandName.includes("\\");
|
|
159
|
+
if (!looksLikeFilepath) {
|
|
160
|
+
console.log(`Unknown command "/${commandName}". Available commands: ${availableCommands
|
|
161
|
+
.map((cmd) => `/${cmd.name}`)
|
|
162
|
+
.join(", ")}`);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
160
165
|
}
|
|
161
166
|
}
|
|
162
167
|
for (const module of this.modules) {
|