@bastani/atomic 0.6.5 → 0.6.6-1
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/.agents/skills/ado-commit/SKILL.md +2 -0
- package/.agents/skills/ado-create-pr/SKILL.md +2 -0
- package/.agents/skills/advanced-evaluation/SKILL.md +2 -0
- package/.agents/skills/ast-grep/SKILL.md +2 -0
- package/.agents/skills/bdi-mental-states/SKILL.md +2 -0
- package/.agents/skills/bun/SKILL.md +156 -122
- package/.agents/skills/context-compression/SKILL.md +2 -0
- package/.agents/skills/context-degradation/SKILL.md +2 -0
- package/.agents/skills/context-fundamentals/SKILL.md +2 -0
- package/.agents/skills/context-optimization/SKILL.md +2 -0
- package/.agents/skills/create-spec/SKILL.md +2 -0
- package/.agents/skills/docx/SKILL.md +2 -0
- package/.agents/skills/evaluation/SKILL.md +2 -0
- package/.agents/skills/explain-code/SKILL.md +2 -0
- package/.agents/skills/filesystem-context/SKILL.md +2 -0
- package/.agents/skills/find-skills/SKILL.md +2 -0
- package/.agents/skills/gh-commit/SKILL.md +2 -0
- package/.agents/skills/gh-create-pr/SKILL.md +2 -0
- package/.agents/skills/hosted-agents/SKILL.md +2 -0
- package/.agents/skills/impeccable/SKILL.md +117 -304
- package/.agents/skills/impeccable/agents/openai.yaml +4 -0
- package/.agents/skills/{adapt/SKILL.md → impeccable/reference/adapt.md} +2 -11
- package/.agents/skills/{animate/SKILL.md → impeccable/reference/animate.md} +15 -15
- package/.agents/skills/{audit/SKILL.md → impeccable/reference/audit.md} +8 -22
- package/.agents/skills/{bolder/SKILL.md → impeccable/reference/bolder.md} +9 -13
- package/.agents/skills/impeccable/reference/brand.md +114 -0
- package/.agents/skills/{clarify/SKILL.md → impeccable/reference/clarify.md} +2 -11
- package/.agents/skills/{colorize/SKILL.md → impeccable/reference/colorize.md} +23 -12
- package/.agents/skills/impeccable/reference/craft.md +152 -29
- package/.agents/skills/{critique/SKILL.md → impeccable/reference/critique.md} +25 -37
- package/.agents/skills/{delight/SKILL.md → impeccable/reference/delight.md} +9 -11
- package/.agents/skills/{distill/SKILL.md → impeccable/reference/distill.md} +2 -13
- package/.agents/skills/impeccable/reference/document.md +427 -0
- package/.agents/skills/impeccable/reference/extract.md +1 -1
- package/.agents/skills/{harden/SKILL.md → impeccable/reference/harden.md} +1 -43
- package/.agents/skills/{layout/SKILL.md → impeccable/reference/layout.md} +27 -11
- package/.agents/skills/impeccable/reference/live.md +594 -0
- package/.agents/skills/impeccable/reference/motion-design.md +12 -2
- package/.agents/skills/impeccable/reference/onboard.md +234 -0
- package/.agents/skills/{optimize/SKILL.md → impeccable/reference/optimize.md} +4 -12
- package/.agents/skills/{overdrive/SKILL.md → impeccable/reference/overdrive.md} +9 -21
- package/.agents/skills/{critique → impeccable}/reference/personas.md +1 -1
- package/.agents/skills/{polish/SKILL.md → impeccable/reference/polish.md} +31 -23
- package/.agents/skills/impeccable/reference/product.md +62 -0
- package/.agents/skills/{quieter/SKILL.md → impeccable/reference/quieter.md} +7 -11
- package/.agents/skills/impeccable/reference/shape.md +151 -0
- package/.agents/skills/impeccable/reference/teach.md +156 -0
- package/.agents/skills/{typeset/SKILL.md → impeccable/reference/typeset.md} +19 -11
- package/.agents/skills/impeccable/reference/typography.md +31 -14
- package/.agents/skills/impeccable/scripts/cleanup-deprecated.mjs +87 -17
- package/.agents/skills/impeccable/scripts/command-metadata.json +94 -0
- package/.agents/skills/impeccable/scripts/design-parser.mjs +820 -0
- package/.agents/skills/impeccable/scripts/detect-csp.mjs +198 -0
- package/.agents/skills/impeccable/scripts/is-generated.mjs +69 -0
- package/.agents/skills/impeccable/scripts/live-accept.mjs +595 -0
- package/.agents/skills/impeccable/scripts/live-browser.js +4781 -0
- package/.agents/skills/impeccable/scripts/live-inject.mjs +445 -0
- package/.agents/skills/impeccable/scripts/live-poll.mjs +186 -0
- package/.agents/skills/impeccable/scripts/live-server.mjs +694 -0
- package/.agents/skills/impeccable/scripts/live-wrap.mjs +571 -0
- package/.agents/skills/impeccable/scripts/live.mjs +247 -0
- package/.agents/skills/impeccable/scripts/load-context.mjs +141 -0
- package/.agents/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
- package/.agents/skills/impeccable/scripts/pin.mjs +214 -0
- package/.agents/skills/init/SKILL.md +2 -0
- package/.agents/skills/liteparse/SKILL.md +1 -0
- package/.agents/skills/memory-systems/SKILL.md +2 -0
- package/.agents/skills/multi-agent-patterns/SKILL.md +2 -0
- package/.agents/skills/opentui/SKILL.md +1 -0
- package/.agents/skills/pdf/SKILL.md +2 -0
- package/.agents/skills/playwright-cli/SKILL.md +51 -5
- package/.agents/skills/playwright-cli/references/playwright-tests.md +1 -1
- package/.agents/skills/playwright-cli/references/running-code.md +10 -0
- package/.agents/skills/playwright-cli/references/session-management.md +56 -0
- package/.agents/skills/playwright-cli/references/spec-driven-testing.md +305 -0
- package/.agents/skills/playwright-cli/references/test-generation.md +49 -3
- package/.agents/skills/pptx/SKILL.md +2 -0
- package/.agents/skills/project-development/SKILL.md +2 -0
- package/.agents/skills/prompt-engineer/SKILL.md +2 -0
- package/.agents/skills/research-codebase/SKILL.md +2 -0
- package/.agents/skills/ripgrep/SKILL.md +2 -0
- package/.agents/skills/skill-creator/LICENSE.txt +1 -1
- package/.agents/skills/skill-creator/SKILL.md +2 -0
- package/.agents/skills/sl-commit/SKILL.md +2 -0
- package/.agents/skills/sl-submit-diff/SKILL.md +2 -0
- package/.agents/skills/tdd/SKILL.md +4 -0
- package/.agents/skills/tool-design/SKILL.md +2 -0
- package/.agents/skills/typescript-advanced-types/SKILL.md +2 -1
- package/.agents/skills/typescript-expert/SKILL.md +7 -1
- package/.agents/skills/typescript-react-reviewer/SKILL.md +2 -1
- package/.agents/skills/workflow-creator/SKILL.md +75 -72
- package/.agents/skills/workflow-creator/references/session-config.md +48 -1
- package/.agents/skills/xlsx/SKILL.md +2 -0
- package/.opencode/opencode.json +6 -2
- package/README.md +39 -38
- package/dist/lib/atomic-temp.d.ts +8 -0
- package/dist/lib/atomic-temp.d.ts.map +1 -0
- package/dist/lib/terminal-env.d.ts +9 -0
- package/dist/lib/terminal-env.d.ts.map +1 -0
- package/dist/sdk/providers/claude.d.ts.map +1 -1
- package/dist/sdk/providers/copilot.d.ts +24 -14
- package/dist/sdk/providers/copilot.d.ts.map +1 -1
- package/dist/sdk/runtime/executor.d.ts +8 -0
- package/dist/sdk/runtime/executor.d.ts.map +1 -1
- package/dist/sdk/runtime/port-discovery.d.ts +71 -0
- package/dist/sdk/runtime/port-discovery.d.ts.map +1 -0
- package/dist/sdk/runtime/tmux.d.ts +10 -0
- package/dist/sdk/runtime/tmux.d.ts.map +1 -1
- package/dist/sdk/types.d.ts +1 -0
- package/dist/sdk/types.d.ts.map +1 -1
- package/dist/sdk/workflows/builtin/deep-research-codebase/opencode/index.d.ts.map +1 -1
- package/dist/sdk/workflows/builtin/open-claude-design/opencode/index.d.ts.map +1 -1
- package/dist/sdk/workflows/builtin/ralph/claude/index.d.ts.map +1 -1
- package/dist/sdk/workflows/builtin/ralph/copilot/index.d.ts.map +1 -1
- package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts +15 -0
- package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts.map +1 -1
- package/dist/sdk/workflows/builtin/ralph/opencode/index.d.ts.map +1 -1
- package/package.json +10 -10
- package/src/commands/cli/chat/index.test.ts +194 -2
- package/src/commands/cli/chat/index.ts +83 -28
- package/src/lib/atomic-temp.test.ts +86 -0
- package/src/lib/atomic-temp.ts +62 -0
- package/src/lib/terminal-env.test.ts +343 -0
- package/src/lib/terminal-env.ts +100 -0
- package/src/scripts/clean-dist.test.ts +53 -0
- package/src/scripts/clean-dist.ts +37 -0
- package/src/sdk/providers/claude.ts +42 -20
- package/src/sdk/providers/copilot.test.ts +365 -0
- package/src/sdk/providers/copilot.ts +117 -15
- package/src/sdk/runtime/cc-debounce.ts +2 -2
- package/src/sdk/runtime/executor.test.ts +322 -1
- package/src/sdk/runtime/executor.ts +159 -96
- package/src/sdk/runtime/port-discovery.test.ts +573 -0
- package/src/sdk/runtime/port-discovery.ts +496 -0
- package/src/sdk/runtime/tmux.ts +22 -2
- package/src/sdk/types.ts +1 -0
- package/src/sdk/workflows/builtin/deep-research-codebase/opencode/index.ts +24 -6
- package/src/sdk/workflows/builtin/open-claude-design/opencode/index.ts +52 -13
- package/src/sdk/workflows/builtin/ralph/claude/index.ts +31 -3
- package/src/sdk/workflows/builtin/ralph/copilot/index.ts +16 -0
- package/src/sdk/workflows/builtin/ralph/helpers/prompts.ts +70 -3
- package/src/sdk/workflows/builtin/ralph/opencode/index.ts +50 -6
- package/src/services/system/auth.test.ts +53 -0
- package/src/services/system/auth.ts +31 -28
- package/src/services/system/detect.ts +1 -1
- package/.agents/skills/shape/SKILL.md +0 -96
- /package/.agents/skills/{critique → impeccable}/reference/cognitive-load.md +0 -0
- /package/.agents/skills/{critique → impeccable}/reference/heuristics-scoring.md +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { test, expect, describe, beforeEach, afterEach } from "bun:test";
|
|
1
|
+
import { test, expect, describe, beforeEach, afterEach, mock } from "bun:test";
|
|
2
2
|
import { mkdtempSync, writeFileSync, chmodSync, rmSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
discoverCopilotBinary,
|
|
14
14
|
applyContainerEnvDefaults,
|
|
15
15
|
normalizeExternalCopilotOptions,
|
|
16
|
+
buildPaneCommand,
|
|
17
|
+
waitForServer,
|
|
16
18
|
type CopilotHILSessionSurface,
|
|
17
19
|
} from "./executor.ts";
|
|
18
20
|
import type { SavedMessage } from "../types.ts";
|
|
@@ -998,3 +1000,322 @@ describe("discoverCopilotBinary / shouldOverrideCopilotCliPath", () => {
|
|
|
998
1000
|
expect(process.env.COPILOT_CLI_PATH).toBe("/custom/copilot");
|
|
999
1001
|
});
|
|
1000
1002
|
});
|
|
1003
|
+
|
|
1004
|
+
// ---------------------------------------------------------------------------
|
|
1005
|
+
// buildPaneCommand
|
|
1006
|
+
// ---------------------------------------------------------------------------
|
|
1007
|
+
|
|
1008
|
+
describe("buildPaneCommand", () => {
|
|
1009
|
+
test("copilot: command contains --ui-server and --port 0", () => {
|
|
1010
|
+
const { command } = buildPaneCommand("copilot");
|
|
1011
|
+
expect(command).toContain("--ui-server");
|
|
1012
|
+
expect(command).toContain("--port 0");
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
test("copilot: command invokes the copilot binary as the first token", () => {
|
|
1016
|
+
const { command } = buildPaneCommand("copilot");
|
|
1017
|
+
// Accept either a bare name (binary not on PATH in test env) or an
|
|
1018
|
+
// absolute path resolved via Bun.which — both end in "copilot".
|
|
1019
|
+
expect(command).toMatch(/^("[^"]*\/)?[^\s"]*copilot"?\s/);
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
test("opencode: command contains --port 0 but not --ui-server", () => {
|
|
1023
|
+
const { command } = buildPaneCommand("opencode");
|
|
1024
|
+
expect(command).toContain("--port 0");
|
|
1025
|
+
expect(command).not.toContain("--ui-server");
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
test("opencode: command invokes the opencode binary as the first token", () => {
|
|
1029
|
+
const { command } = buildPaneCommand("opencode");
|
|
1030
|
+
expect(command).toMatch(/^("[^"]*\/)?[^\s"]*opencode"?\s/);
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
test("claude: command resolves to a shell and does not contain --port", () => {
|
|
1034
|
+
const { command } = buildPaneCommand("claude");
|
|
1035
|
+
// SHELL is typically already absolute (e.g. /bin/zsh) so it passes through
|
|
1036
|
+
// unchanged; bare fallbacks ("sh"/"pwsh") get resolved via Bun.which.
|
|
1037
|
+
const expected =
|
|
1038
|
+
process.env.SHELL || (process.platform === "win32" ? "pwsh" : "sh");
|
|
1039
|
+
const stripped = command.replace(/^"|"$/g, "");
|
|
1040
|
+
if (expected.includes("/") || expected.includes("\\")) {
|
|
1041
|
+
expect(stripped).toBe(expected);
|
|
1042
|
+
} else {
|
|
1043
|
+
// Bare fallback either resolved via Bun.which or returned as-is.
|
|
1044
|
+
expect(stripped.endsWith(expected) || stripped === expected).toBe(true);
|
|
1045
|
+
}
|
|
1046
|
+
expect(command).not.toContain("--port");
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
test("claude: scopes temp files to the user's Atomic temp directory", () => {
|
|
1050
|
+
const { envVars } = buildPaneCommand("claude");
|
|
1051
|
+
expect(envVars.TMPDIR).toMatch(/\/\.atomic\/tmp$/);
|
|
1052
|
+
expect(envVars.TMP).toBe(envVars.TMPDIR);
|
|
1053
|
+
expect(envVars.TEMP).toBe(envVars.TMPDIR);
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
test("claude: explicit temp env overrides the Atomic default", () => {
|
|
1057
|
+
const { envVars } = buildPaneCommand("claude", {
|
|
1058
|
+
envVars: { TMPDIR: "/custom/tmp", TMP: "/custom/tmp", TEMP: "/custom/tmp" },
|
|
1059
|
+
});
|
|
1060
|
+
expect(envVars.TMPDIR).toBe("/custom/tmp");
|
|
1061
|
+
expect(envVars.TMP).toBe("/custom/tmp");
|
|
1062
|
+
expect(envVars.TEMP).toBe("/custom/tmp");
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
test("overrides.envVars merges with defaults for copilot", () => {
|
|
1066
|
+
const { envVars } = buildPaneCommand("copilot", {
|
|
1067
|
+
envVars: { MY_VAR: "hello" },
|
|
1068
|
+
});
|
|
1069
|
+
// Default copilot env var preserved
|
|
1070
|
+
expect(envVars.COPILOT_ALLOW_ALL).toBe("true");
|
|
1071
|
+
// Override merged in
|
|
1072
|
+
expect(envVars.MY_VAR).toBe("hello");
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
test("overrides.chatFlags replaces defaults for copilot", () => {
|
|
1076
|
+
const { command } = buildPaneCommand("copilot", {
|
|
1077
|
+
chatFlags: ["--custom-flag"],
|
|
1078
|
+
});
|
|
1079
|
+
expect(command).toContain("--custom-flag");
|
|
1080
|
+
// Default flags should be absent
|
|
1081
|
+
expect(command).not.toContain("--add-dir");
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
test("extraChatFlags appended to copilot command", () => {
|
|
1085
|
+
const { command } = buildPaneCommand("copilot", {}, ["--extra-flag"]);
|
|
1086
|
+
expect(command).toContain("--extra-flag");
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
test("extraChatFlags not appended to opencode command", () => {
|
|
1090
|
+
const { command } = buildPaneCommand("opencode", {}, ["--extra-flag"]);
|
|
1091
|
+
expect(command).not.toContain("--extra-flag");
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
test("copilot: respects COPILOT_CLI_PATH env var for binary resolution", () => {
|
|
1095
|
+
const origCliPath = process.env["COPILOT_CLI_PATH"];
|
|
1096
|
+
process.env["COPILOT_CLI_PATH"] = "/custom/path/copilot";
|
|
1097
|
+
try {
|
|
1098
|
+
const { command } = buildPaneCommand("copilot");
|
|
1099
|
+
// The command should start with the COPILOT_CLI_PATH binary.
|
|
1100
|
+
expect(command.startsWith("/custom/path/copilot ")).toBe(true);
|
|
1101
|
+
} finally {
|
|
1102
|
+
if (origCliPath === undefined) delete process.env["COPILOT_CLI_PATH"];
|
|
1103
|
+
else process.env["COPILOT_CLI_PATH"] = origCliPath;
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
// ---------------------------------------------------------------------------
|
|
1109
|
+
// waitForServer
|
|
1110
|
+
// ---------------------------------------------------------------------------
|
|
1111
|
+
|
|
1112
|
+
// Three non-empty lines — enough to break the TUI-render loop.
|
|
1113
|
+
const PANE_CONTENT_READY = "line one\nline two\nline three\n";
|
|
1114
|
+
|
|
1115
|
+
// ---------------------------------------------------------------------------
|
|
1116
|
+
// waitForServer — module-level captures for mock.module cleanup
|
|
1117
|
+
// ---------------------------------------------------------------------------
|
|
1118
|
+
|
|
1119
|
+
// Snapshot the real function values BEFORE any mock.module call.
|
|
1120
|
+
// We can't hold the module namespace reference because mock.module mutates it
|
|
1121
|
+
// in-place, so we must copy the property values into a plain object snapshot.
|
|
1122
|
+
const _tmuxMod = await import("./tmux.ts");
|
|
1123
|
+
const realTmuxSnapshot = { ..._tmuxMod };
|
|
1124
|
+
const _portMod = await import("./port-discovery.ts");
|
|
1125
|
+
const realPortDiscoverySnapshot = { ..._portMod };
|
|
1126
|
+
let realCopilotSdkSnapshot: Record<string, unknown> | null = null;
|
|
1127
|
+
try {
|
|
1128
|
+
const _copilotMod = await import("@github/copilot-sdk");
|
|
1129
|
+
realCopilotSdkSnapshot = { ..._copilotMod };
|
|
1130
|
+
} catch {
|
|
1131
|
+
// optional dependency — not installed in all environments
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
describe("waitForServer", () => {
|
|
1135
|
+
// Save and restore Bun.sleep so we can make it instant in async tests.
|
|
1136
|
+
let originalSleep: typeof Bun.sleep;
|
|
1137
|
+
|
|
1138
|
+
beforeEach(() => {
|
|
1139
|
+
originalSleep = Bun.sleep;
|
|
1140
|
+
// Make Bun.sleep a no-op so probe retry loops resolve immediately.
|
|
1141
|
+
(globalThis as { Bun: { sleep: (ms: number) => Promise<void> } }).Bun.sleep =
|
|
1142
|
+
() => Promise.resolve();
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
afterEach(() => {
|
|
1146
|
+
(globalThis as { Bun: { sleep: typeof Bun.sleep } }).Bun.sleep =
|
|
1147
|
+
originalSleep;
|
|
1148
|
+
// Restore module mocks so they don't leak into subsequent test files.
|
|
1149
|
+
// Use snapshot copies (not live references) because mock.module mutates
|
|
1150
|
+
// the module namespace in-place.
|
|
1151
|
+
mock.module("./tmux.ts", () => realTmuxSnapshot);
|
|
1152
|
+
mock.module("./port-discovery.ts", () => realPortDiscoverySnapshot);
|
|
1153
|
+
if (realCopilotSdkSnapshot !== null) {
|
|
1154
|
+
mock.module("@github/copilot-sdk", () => realCopilotSdkSnapshot!);
|
|
1155
|
+
}
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
test('returns "" immediately for agent "claude" without touching tmux', async () => {
|
|
1159
|
+
// No mocks for tmux — any call would throw because real tmux isn't running.
|
|
1160
|
+
const result = await waitForServer("claude", "%0");
|
|
1161
|
+
expect(result).toBe("");
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
test("copilot: throws when getPanePid returns null", async () => {
|
|
1165
|
+
mock.module("./tmux.ts", () => ({
|
|
1166
|
+
capturePane: () => PANE_CONTENT_READY,
|
|
1167
|
+
getPanePid: () => null,
|
|
1168
|
+
// preserve other named exports as stubs
|
|
1169
|
+
spawnMuxAttach: () => {},
|
|
1170
|
+
}));
|
|
1171
|
+
|
|
1172
|
+
let err: Error | undefined;
|
|
1173
|
+
try {
|
|
1174
|
+
await waitForServer("copilot", "%0");
|
|
1175
|
+
} catch (e) {
|
|
1176
|
+
err = e as Error;
|
|
1177
|
+
}
|
|
1178
|
+
expect(err?.message).toContain("failed to resolve agent PID");
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
test("copilot: throws when port discovery times out (getListeningPortForPid returns null)", async () => {
|
|
1182
|
+
mock.module("./tmux.ts", () => ({
|
|
1183
|
+
capturePane: () => PANE_CONTENT_READY,
|
|
1184
|
+
getPanePid: () => 12345,
|
|
1185
|
+
spawnMuxAttach: () => {},
|
|
1186
|
+
}));
|
|
1187
|
+
|
|
1188
|
+
mock.module("./port-discovery.ts", () => ({
|
|
1189
|
+
getListeningPortForPid: async () => null,
|
|
1190
|
+
PORT_DISCOVERY_TIMEOUT_MS: 100,
|
|
1191
|
+
}));
|
|
1192
|
+
|
|
1193
|
+
let err: Error | undefined;
|
|
1194
|
+
try {
|
|
1195
|
+
await waitForServer("copilot", "%0");
|
|
1196
|
+
} catch (e) {
|
|
1197
|
+
err = e as Error;
|
|
1198
|
+
}
|
|
1199
|
+
expect(err?.message).toContain("did not bind a TCP port");
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
test("copilot: throws when SDK probe fails (CopilotClient.start rejects)", async () => {
|
|
1203
|
+
mock.module("./tmux.ts", () => ({
|
|
1204
|
+
capturePane: () => PANE_CONTENT_READY,
|
|
1205
|
+
getPanePid: () => 12345,
|
|
1206
|
+
spawnMuxAttach: () => {},
|
|
1207
|
+
}));
|
|
1208
|
+
|
|
1209
|
+
mock.module("./port-discovery.ts", () => ({
|
|
1210
|
+
getListeningPortForPid: async () => 50001,
|
|
1211
|
+
PORT_DISCOVERY_TIMEOUT_MS: 100,
|
|
1212
|
+
}));
|
|
1213
|
+
|
|
1214
|
+
mock.module("@github/copilot-sdk", () => ({
|
|
1215
|
+
CopilotClient: class {
|
|
1216
|
+
start() {
|
|
1217
|
+
return Promise.reject(new Error("connection refused"));
|
|
1218
|
+
}
|
|
1219
|
+
listSessions() {
|
|
1220
|
+
return Promise.resolve([]);
|
|
1221
|
+
}
|
|
1222
|
+
stop() {
|
|
1223
|
+
return Promise.resolve();
|
|
1224
|
+
}
|
|
1225
|
+
},
|
|
1226
|
+
}));
|
|
1227
|
+
|
|
1228
|
+
// SERVER_PROBE_TIMEOUT_MS is 60_000 but Bun.sleep is mocked to instant,
|
|
1229
|
+
// so the loop burns through retries until Date.now() passes the deadline.
|
|
1230
|
+
// To avoid a real 60s wall-clock wait we mock Date.now temporarily.
|
|
1231
|
+
const realDateNow = Date.now;
|
|
1232
|
+
let callCount = 0;
|
|
1233
|
+
Date.now = () => {
|
|
1234
|
+
callCount++;
|
|
1235
|
+
// First few calls (port-deadline checks, probe-deadline setup): allow.
|
|
1236
|
+
// After 10 calls assume probe deadline has passed.
|
|
1237
|
+
return callCount > 10 ? realDateNow() + 999_999 : realDateNow();
|
|
1238
|
+
};
|
|
1239
|
+
|
|
1240
|
+
let err: Error | undefined;
|
|
1241
|
+
try {
|
|
1242
|
+
try {
|
|
1243
|
+
await waitForServer("copilot", "%0");
|
|
1244
|
+
} catch (e) {
|
|
1245
|
+
err = e as Error;
|
|
1246
|
+
}
|
|
1247
|
+
} finally {
|
|
1248
|
+
Date.now = realDateNow;
|
|
1249
|
+
}
|
|
1250
|
+
expect(err?.message).toContain("copilot SDK probe did not respond");
|
|
1251
|
+
});
|
|
1252
|
+
|
|
1253
|
+
test("copilot: returns localhost:<port> when SDK probe succeeds", async () => {
|
|
1254
|
+
mock.module("./tmux.ts", () => ({
|
|
1255
|
+
capturePane: () => PANE_CONTENT_READY,
|
|
1256
|
+
getPanePid: () => 12345,
|
|
1257
|
+
spawnMuxAttach: () => {},
|
|
1258
|
+
}));
|
|
1259
|
+
|
|
1260
|
+
mock.module("./port-discovery.ts", () => ({
|
|
1261
|
+
getListeningPortForPid: async () => 50001,
|
|
1262
|
+
PORT_DISCOVERY_TIMEOUT_MS: 100,
|
|
1263
|
+
}));
|
|
1264
|
+
|
|
1265
|
+
mock.module("@github/copilot-sdk", () => ({
|
|
1266
|
+
CopilotClient: class {
|
|
1267
|
+
start() {
|
|
1268
|
+
return Promise.resolve();
|
|
1269
|
+
}
|
|
1270
|
+
listSessions() {
|
|
1271
|
+
return Promise.resolve([]);
|
|
1272
|
+
}
|
|
1273
|
+
stop() {
|
|
1274
|
+
return Promise.resolve();
|
|
1275
|
+
}
|
|
1276
|
+
},
|
|
1277
|
+
}));
|
|
1278
|
+
|
|
1279
|
+
const result = await waitForServer("copilot", "%0");
|
|
1280
|
+
expect(result).toBe("localhost:50001");
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
test("copilot: probe does not pass useLoggedInUser to CopilotClient (external server owns auth)", async () => {
|
|
1284
|
+
mock.module("./tmux.ts", () => ({
|
|
1285
|
+
capturePane: () => PANE_CONTENT_READY,
|
|
1286
|
+
getPanePid: () => 12345,
|
|
1287
|
+
spawnMuxAttach: () => {},
|
|
1288
|
+
}));
|
|
1289
|
+
|
|
1290
|
+
mock.module("./port-discovery.ts", () => ({
|
|
1291
|
+
getListeningPortForPid: async () => 50002,
|
|
1292
|
+
PORT_DISCOVERY_TIMEOUT_MS: 100,
|
|
1293
|
+
}));
|
|
1294
|
+
|
|
1295
|
+
let capturedOptions: unknown;
|
|
1296
|
+
mock.module("@github/copilot-sdk", () => ({
|
|
1297
|
+
CopilotClient: class {
|
|
1298
|
+
constructor(opts: unknown) {
|
|
1299
|
+
capturedOptions = opts;
|
|
1300
|
+
}
|
|
1301
|
+
start() {
|
|
1302
|
+
return Promise.resolve();
|
|
1303
|
+
}
|
|
1304
|
+
listSessions() {
|
|
1305
|
+
return Promise.resolve([]);
|
|
1306
|
+
}
|
|
1307
|
+
stop() {
|
|
1308
|
+
return Promise.resolve();
|
|
1309
|
+
}
|
|
1310
|
+
},
|
|
1311
|
+
}));
|
|
1312
|
+
|
|
1313
|
+
await waitForServer("copilot", "%0");
|
|
1314
|
+
const opts = capturedOptions as Record<string, unknown>;
|
|
1315
|
+
expect(opts).toBeDefined();
|
|
1316
|
+
// cliUrl must be set — connecting to an existing server
|
|
1317
|
+
expect(opts["cliUrl"]).toBe("localhost:50002");
|
|
1318
|
+
// useLoggedInUser must NOT be set — external server owns auth
|
|
1319
|
+
expect(Object.prototype.hasOwnProperty.call(opts, "useLoggedInUser")).toBe(false);
|
|
1320
|
+
});
|
|
1321
|
+
});
|