@harnessforce/cli 0.2.0 → 0.3.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/README.md +33 -11
- package/dist/bin.js +618 -443
- package/package.json +5 -5
package/dist/bin.js
CHANGED
|
@@ -3,15 +3,15 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
import { constants, homedir, tmpdir } from "node:os";
|
|
4
4
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
5
5
|
import { basename, dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
|
|
6
|
-
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
|
-
import { access, chmod, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, utimes, writeFile } from "node:fs/promises";
|
|
6
|
+
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
7
|
+
import { access, chmod, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, utimes, writeFile } from "node:fs/promises";
|
|
8
8
|
import { execFile, spawn } from "node:child_process";
|
|
9
9
|
import { constants as constants$1, createReadStream } from "node:fs";
|
|
10
10
|
import { createServer } from "node:http";
|
|
11
|
-
import { ATTR, INTERVENTION_KINDS, LOOP_KINDS, PROPOSAL_KINDS, isToken, normalizeRepository } from "@harnessforce/semconv";
|
|
11
|
+
import { ATTR, INTERVENTION_KINDS, LOOP_KINDS, MAX_WORK_LOCATIONS, PROPOSAL_KINDS, isToken, normalizeRepository } from "@harnessforce/semconv";
|
|
12
12
|
import { createInterface } from "node:readline";
|
|
13
13
|
import { isDeepStrictEqual } from "node:util";
|
|
14
|
-
|
|
14
|
+
// ../agent-core/src/config/canonical.ts
|
|
15
15
|
const byCodeUnit$1 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
16
16
|
function canonicalJson(value) {
|
|
17
17
|
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
@@ -28,11 +28,9 @@ function sortComponents(components) {
|
|
|
28
28
|
return [...components].sort((a, b) => byCodeUnit$1(a.kind, b.kind) || byCodeUnit$1(a.source, b.source) || byCodeUnit$1(a.id, b.id));
|
|
29
29
|
}
|
|
30
30
|
const snapshotId = (components) => hashValue(sortComponents(components));
|
|
31
|
-
|
|
32
|
-
//#region ../agent-core/src/object.ts
|
|
31
|
+
// ../agent-core/src/object.ts
|
|
33
32
|
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34
|
-
|
|
35
|
-
//#region ../agent-core/src/config/files.ts
|
|
33
|
+
// ../agent-core/src/config/files.ts
|
|
36
34
|
const MAX_DEPTH = 8;
|
|
37
35
|
const MAX_FILE_BYTES = 1048576;
|
|
38
36
|
var CollectionExpired = class extends Error {};
|
|
@@ -93,8 +91,7 @@ async function listDirectories(dir, guard) {
|
|
|
93
91
|
for (const entry of await entries(dir, guard)) if (await kindOf(dir, entry, guard) === "directory") names.push(entry.name);
|
|
94
92
|
return names;
|
|
95
93
|
}
|
|
96
|
-
|
|
97
|
-
//#region ../agent-core/src/managed.ts
|
|
94
|
+
// ../agent-core/src/managed.ts
|
|
98
95
|
const MANAGED_DIRS = {
|
|
99
96
|
darwin: "/Library/Application Support/ClaudeCode",
|
|
100
97
|
win32: "C:\\Program Files\\ClaudeCode"
|
|
@@ -113,8 +110,7 @@ async function readManagedEnv(managedDir, names) {
|
|
|
113
110
|
}
|
|
114
111
|
return values;
|
|
115
112
|
}
|
|
116
|
-
|
|
117
|
-
//#region ../agent-core/src/process/lookup.ts
|
|
113
|
+
// ../agent-core/src/process/lookup.ts
|
|
118
114
|
const DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
119
115
|
const NPM_SHIM_MAX_BYTES = 65536;
|
|
120
116
|
const SHEBANG_MAX_BYTES = 256;
|
|
@@ -295,8 +291,7 @@ async function commandLineFor(file, args, context, { quoteArgs = true } = {}) {
|
|
|
295
291
|
verbatim: true
|
|
296
292
|
};
|
|
297
293
|
}
|
|
298
|
-
|
|
299
|
-
//#region ../agent-core/src/process/git.ts
|
|
294
|
+
// ../agent-core/src/process/git.ts
|
|
300
295
|
const GIT_TIMEOUT_MS$1 = 1e3;
|
|
301
296
|
const execGit = (file, args, env, timeoutMs) => new Promise((resolve) => {
|
|
302
297
|
execFile(file, args, {
|
|
@@ -334,8 +329,7 @@ function createGitRunner({ platform, env, processCwd, excludeTarget, timeoutMs =
|
|
|
334
329
|
], gitEnv, timeoutMs);
|
|
335
330
|
};
|
|
336
331
|
}
|
|
337
|
-
|
|
338
|
-
//#region ../agent-core/src/process/runtime-env.ts
|
|
332
|
+
// ../agent-core/src/process/runtime-env.ts
|
|
339
333
|
const RUNTIME_VARIABLES = [
|
|
340
334
|
"NODE_TLS_REJECT_UNAUTHORIZED",
|
|
341
335
|
"NODE_EXTRA_CA_CERTS",
|
|
@@ -412,8 +406,7 @@ function takeStashedRuntimeEnv(env, platform) {
|
|
|
412
406
|
if (!isObject(value)) return {};
|
|
413
407
|
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string" && isRuntimeVariable(entry[0], platform)));
|
|
414
408
|
}
|
|
415
|
-
|
|
416
|
-
//#region src/shared/messages.ts
|
|
409
|
+
// src/shared/messages.ts
|
|
417
410
|
const INIT_MESSAGES = {
|
|
418
411
|
success: "Connected to Harnessforce. Restart Claude Code to apply the settings",
|
|
419
412
|
pluginAutoUpdateOn: "Automatic updates for the Harnessforce plugin are on. To turn them off, run `harnessforce init --no-plugin-auto-update` or use /plugin",
|
|
@@ -432,25 +425,32 @@ const INIT_MESSAGES = {
|
|
|
432
425
|
noWorkspace: "You are not a member of any Workspace. Create a Workspace or accept an invitation, then run `harnessforce init` again",
|
|
433
426
|
loginFailed: "The login failed. Run `harnessforce init` again",
|
|
434
427
|
viewer: "The view-only role cannot issue an ingest key. Contact a Workspace administrator",
|
|
435
|
-
limitOwnerAdmin: "The number of active ingest keys has reached the limit.
|
|
428
|
+
limitOwnerAdmin: "The number of active ingest keys has reached the limit. Ask members to revoke unused keys in My ingest keys on their account page",
|
|
436
429
|
limitMember: "The ingest key limit has been reached. Contact a Workspace administrator",
|
|
437
430
|
gateUnavailable: "Could not apply the change on the receiving side. Nothing was changed. Try again",
|
|
438
431
|
ingestUnavailable: "This environment does not receive telemetry, so an ingest key cannot be issued",
|
|
439
432
|
saveFailed: "Could not save the ingest key. Run `harnessforce init` again",
|
|
440
433
|
codexSaveFailed: "Could not write the Codex configuration. Run `harnessforce init` again",
|
|
441
|
-
|
|
442
|
-
|
|
434
|
+
contentNotStored: "This workspace does not store content, so sending content was not enabled. An owner or admin can turn it on under Collection & retention in the workspace settings, then run `harnessforce init --send-content` again",
|
|
435
|
+
contentNotSent: "Content is not sent from this machine. Run `harnessforce init --send-content` to send it"
|
|
443
436
|
};
|
|
444
437
|
const codexNotes = (configPath) => [
|
|
445
438
|
`Wrote the Codex configuration (${configPath}). Restart Codex to apply it`,
|
|
446
|
-
"Codex hooks do not run until you trust them in Codex's /hooks. Trust the harnessforce SessionStart
|
|
439
|
+
"Codex hooks do not run until you trust them in Codex's /hooks. Trust the harnessforce SessionStart, PostToolUse, and Stop hooks in /hooks",
|
|
447
440
|
"To use the Harnessforce MCP server, log in with `codex mcp login harnessforce`"
|
|
448
441
|
].join("\n");
|
|
449
442
|
const nonDefaultConnectionMessage = (url) => `Connecting to ${url} (not the default production endpoint)`;
|
|
450
443
|
const listenFailedMessage = (port) => `${INIT_MESSAGES.listenFailed}. Stop the process using port ${port}, or specify another port with \`harnessforce init --port\``;
|
|
451
444
|
const keychainUnavailableMessage = (keychain, message = INIT_MESSAGES.keychainUnavailable) => keychain.unavailableHint ? `${message}. ${keychain.unavailableHint}` : message;
|
|
452
|
-
|
|
453
|
-
|
|
445
|
+
const CONTENT_READERS = {
|
|
446
|
+
all_members: "Everyone in the workspace can read it",
|
|
447
|
+
owners_admins_and_sender: "Owners, admins and you can read it",
|
|
448
|
+
sender_only: "Within the workspace, only you can read it"
|
|
449
|
+
};
|
|
450
|
+
function contentEnabledMessage(content) {
|
|
451
|
+
return `Sending ${[...content.isPromptsStored ? ["the prompts you type"] : [], ...content.isModelAndToolsStored ? ["model responses and tool input and output"] : []].join(", ")} is now enabled. Values that match known patterns for secrets and personal information are masked, but not everything can be detected. ${CONTENT_READERS[content.visibility]}. It is deleted after ${content.retentionDays} days`;
|
|
452
|
+
}
|
|
453
|
+
// src/credentials/keychain.ts
|
|
454
454
|
async function openSingleStore(store) {
|
|
455
455
|
if (!await store.isAvailable()) return void 0;
|
|
456
456
|
return {
|
|
@@ -476,8 +476,7 @@ const workspaceAccounts = (workspaceId) => [
|
|
|
476
476
|
];
|
|
477
477
|
const isIngestKeyAccount = (account) => account.endsWith(INGEST_KEY_SUFFIX);
|
|
478
478
|
const apiTokenWorkspaceId = (account) => account.endsWith(API_TOKEN_SUFFIX) ? account.slice(0, -10) : void 0;
|
|
479
|
-
|
|
480
|
-
//#region src/credentials/linux-keychain.ts
|
|
479
|
+
// src/credentials/linux-keychain.ts
|
|
481
480
|
const SECRET_SERVICE_TIMEOUT_MS = 6e4;
|
|
482
481
|
const WAITING_NOTICE_DELAY_MS = 2e3;
|
|
483
482
|
const PROBE_ACCOUNT$1 = "availability-probe";
|
|
@@ -631,8 +630,7 @@ async function migrate({ kernel, secretService }, workspaceId) {
|
|
|
631
630
|
clearTimeout(timer);
|
|
632
631
|
}
|
|
633
632
|
}
|
|
634
|
-
|
|
635
|
-
//#region src/credentials/os-keychain.ts
|
|
633
|
+
// src/credentials/os-keychain.ts
|
|
636
634
|
const PROBE_ACCOUNT = "availability-probe";
|
|
637
635
|
function createOsKeychain({ platform, load, secretService }) {
|
|
638
636
|
let loaded;
|
|
@@ -714,8 +712,7 @@ const unsupported = {
|
|
|
714
712
|
withTimeout: () => unsupported,
|
|
715
713
|
openForInit: async () => void 0
|
|
716
714
|
};
|
|
717
|
-
|
|
718
|
-
//#region src/credentials/secret-service-process.ts
|
|
715
|
+
// src/credentials/secret-service-process.ts
|
|
719
716
|
const CHILD_SCRIPT = `
|
|
720
717
|
const [op, service, account, timeoutMs, modulePath] = process.argv.slice(1);
|
|
721
718
|
setTimeout(() => process.kill(process.pid, "SIGKILL"), Number(timeoutMs));
|
|
@@ -790,8 +787,7 @@ function createSecretServiceProcess({ execPath, resolveKeyring }) {
|
|
|
790
787
|
}
|
|
791
788
|
};
|
|
792
789
|
}
|
|
793
|
-
|
|
794
|
-
//#region src/import/repository.ts
|
|
790
|
+
// src/import/repository.ts
|
|
795
791
|
const GIT_TIMEOUT_MS = 5e3;
|
|
796
792
|
const createImportGit = (platform, env, processCwd) => createGitRunner({
|
|
797
793
|
platform,
|
|
@@ -800,8 +796,7 @@ const createImportGit = (platform, env, processCwd) => createGitRunner({
|
|
|
800
796
|
excludeTarget: false,
|
|
801
797
|
timeoutMs: GIT_TIMEOUT_MS
|
|
802
798
|
});
|
|
803
|
-
|
|
804
|
-
//#region src/init/browser.ts
|
|
799
|
+
// src/init/browser.ts
|
|
805
800
|
const STILL_RUNNING_MS = 3e3;
|
|
806
801
|
function openerFor(platform, url) {
|
|
807
802
|
if (platform === "darwin") return ["open", [url]];
|
|
@@ -838,35 +833,63 @@ async function openBrowser(url, { platform, env, cwd, restoredEnv, spawn: spawnI
|
|
|
838
833
|
child.once("exit", (code) => finish(code === 0));
|
|
839
834
|
});
|
|
840
835
|
}
|
|
841
|
-
|
|
842
|
-
//#region src/init/loopback.ts
|
|
836
|
+
// src/init/loopback.ts
|
|
843
837
|
const HOST = "127.0.0.1";
|
|
844
838
|
const CALLBACK_PATH = "/callback";
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
const
|
|
839
|
+
const COMPLETION_PATH = "/cli/connected";
|
|
840
|
+
function completionUrl(webOrigin, completion) {
|
|
841
|
+
const url = new URL(COMPLETION_PATH, webOrigin);
|
|
842
|
+
url.searchParams.set("status", completion.status);
|
|
843
|
+
if (completion.status === "connected" && completion.workspaceId) url.searchParams.set("workspace", completion.workspaceId);
|
|
844
|
+
return url.href;
|
|
845
|
+
}
|
|
846
|
+
function parseRequestUrl(target) {
|
|
847
|
+
try {
|
|
848
|
+
return new URL(target ?? "/", `http://${HOST}`);
|
|
849
|
+
} catch {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
function isSameState(received, expected) {
|
|
854
|
+
if (received === null) return false;
|
|
855
|
+
const receivedBytes = Buffer.from(received, "utf8");
|
|
856
|
+
const expectedBytes = Buffer.from(expected, "utf8");
|
|
857
|
+
return receivedBytes.length === expectedBytes.length && timingSafeEqual(receivedBytes, expectedBytes);
|
|
858
|
+
}
|
|
848
859
|
async function startLoopback(options) {
|
|
849
860
|
let settle = () => {};
|
|
850
861
|
const callback = new Promise((resolve) => {
|
|
851
862
|
settle = resolve;
|
|
852
863
|
});
|
|
864
|
+
let held;
|
|
853
865
|
const server = createServer((req, res) => {
|
|
854
|
-
const url =
|
|
866
|
+
const url = parseRequestUrl(req.url);
|
|
867
|
+
if (url === void 0) {
|
|
868
|
+
res.writeHead(400).end();
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
855
871
|
if (url.pathname !== CALLBACK_PATH) {
|
|
856
872
|
res.writeHead(404).end();
|
|
857
873
|
return;
|
|
858
874
|
}
|
|
859
|
-
|
|
875
|
+
if (isStopped) {
|
|
876
|
+
res.writeHead(404).end();
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (!isSameState(url.searchParams.get("state"), options.state)) {
|
|
880
|
+
res.writeHead(303, {
|
|
881
|
+
location: completionUrl(options.webOrigin, { status: "failed" }),
|
|
882
|
+
"cache-control": "no-store"
|
|
883
|
+
});
|
|
884
|
+
res.end();
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
held = res;
|
|
888
|
+
stop({
|
|
860
889
|
code: url.searchParams.get("code") || void 0,
|
|
861
890
|
state: url.searchParams.get("state") ?? void 0,
|
|
862
891
|
error: url.searchParams.get("error") ?? void 0
|
|
863
|
-
};
|
|
864
|
-
const isSuccess = !received.error && Boolean(received.code) && received.state === options.state;
|
|
865
|
-
res.writeHead(200, {
|
|
866
|
-
"content-type": "text/html; charset=utf-8",
|
|
867
|
-
"cache-control": "no-store"
|
|
868
892
|
});
|
|
869
|
-
res.end(isSuccess ? SUCCESS_PAGE : FAILURE_PAGE, () => stop(received));
|
|
870
893
|
});
|
|
871
894
|
const timer = setTimeout(() => stop(void 0), options.timeoutMs);
|
|
872
895
|
let isStopped = false;
|
|
@@ -875,9 +898,19 @@ async function startLoopback(options) {
|
|
|
875
898
|
isStopped = true;
|
|
876
899
|
clearTimeout(timer);
|
|
877
900
|
server.close();
|
|
878
|
-
server.closeAllConnections();
|
|
901
|
+
if (!held) server.closeAllConnections();
|
|
879
902
|
settle(received);
|
|
880
903
|
}
|
|
904
|
+
function finish(completion) {
|
|
905
|
+
const response = held;
|
|
906
|
+
held = void 0;
|
|
907
|
+
if (!response) return;
|
|
908
|
+
response.writeHead(303, {
|
|
909
|
+
location: completionUrl(options.webOrigin, completion),
|
|
910
|
+
"cache-control": "no-store"
|
|
911
|
+
});
|
|
912
|
+
response.end(() => server.closeAllConnections());
|
|
913
|
+
}
|
|
881
914
|
await new Promise((resolve, reject) => {
|
|
882
915
|
server.once("error", reject);
|
|
883
916
|
server.listen(options.port, HOST, resolve);
|
|
@@ -888,11 +921,11 @@ async function startLoopback(options) {
|
|
|
888
921
|
const { port } = server.address();
|
|
889
922
|
return {
|
|
890
923
|
redirectUri: `http://${HOST}:${port}${CALLBACK_PATH}`,
|
|
891
|
-
callback
|
|
924
|
+
callback,
|
|
925
|
+
finish
|
|
892
926
|
};
|
|
893
927
|
}
|
|
894
|
-
|
|
895
|
-
//#region ../agent-core/src/url.ts
|
|
928
|
+
// ../agent-core/src/url.ts
|
|
896
929
|
const LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
|
|
897
930
|
"localhost",
|
|
898
931
|
"127.0.0.1",
|
|
@@ -914,8 +947,7 @@ function underBase(base, path) {
|
|
|
914
947
|
return url;
|
|
915
948
|
}
|
|
916
949
|
const withoutExtras = (url) => `${url.origin}${url.pathname.replace(/\/+$/, "")}`;
|
|
917
|
-
|
|
918
|
-
//#region ../agent-core/src/client-update.ts
|
|
950
|
+
// ../agent-core/src/client-update.ts
|
|
919
951
|
const CLIENT_UPDATE_HEADER = "harnessforce-client-update";
|
|
920
952
|
const UPDATE_COMMANDS = {
|
|
921
953
|
cli: "npm install -g @harnessforce/cli@latest",
|
|
@@ -1074,8 +1106,7 @@ async function recheckClientStatus(options) {
|
|
|
1074
1106
|
}
|
|
1075
1107
|
return await readUpdateState(options.statePath, options.version) ?? state;
|
|
1076
1108
|
}
|
|
1077
|
-
|
|
1078
|
-
//#region ../agent-core/src/ingest.ts
|
|
1109
|
+
// ../agent-core/src/ingest.ts
|
|
1079
1110
|
const SEND_TIMEOUT_MS = 2e3;
|
|
1080
1111
|
function ingestBaseFrom(endpoint) {
|
|
1081
1112
|
const base = parseAllowedUrl(endpoint);
|
|
@@ -1110,8 +1141,7 @@ async function postItems(destination, path, items, fetchImpl, timeoutMs = SEND_T
|
|
|
1110
1141
|
};
|
|
1111
1142
|
}
|
|
1112
1143
|
}
|
|
1113
|
-
|
|
1114
|
-
//#region ../agent-core/src/config/hash-input.ts
|
|
1144
|
+
// ../agent-core/src/config/hash-input.ts
|
|
1115
1145
|
function withoutSecrets(entry) {
|
|
1116
1146
|
const { env, headers, url, args: _args, ...rest } = entry;
|
|
1117
1147
|
const result = rest;
|
|
@@ -1143,8 +1173,7 @@ function hashInput(kind, value) {
|
|
|
1143
1173
|
if (kind === "hook" && Array.isArray(value)) return value.map(reduceHookGroup);
|
|
1144
1174
|
return value;
|
|
1145
1175
|
}
|
|
1146
|
-
|
|
1147
|
-
//#region ../agent-core/src/config/scope.ts
|
|
1176
|
+
// ../agent-core/src/config/scope.ts
|
|
1148
1177
|
const MAX_COMPONENTS = 1e3;
|
|
1149
1178
|
var TooManyComponents = class extends Error {};
|
|
1150
1179
|
var Sink = class {
|
|
@@ -1200,11 +1229,9 @@ function collectMcpServers$1(s, servers) {
|
|
|
1200
1229
|
if (!isObject(servers)) return;
|
|
1201
1230
|
for (const [name, value] of Object.entries(servers)) if (isObject(value)) s.addValue("mcp_server", name, value);
|
|
1202
1231
|
}
|
|
1203
|
-
|
|
1204
|
-
//#region ../agent-core/src/plugin-auto-update.ts
|
|
1232
|
+
// ../agent-core/src/plugin-auto-update.ts
|
|
1205
1233
|
const MARKETPLACE_NAME = "harnessforce-agent";
|
|
1206
|
-
|
|
1207
|
-
//#region src/shared/settings.ts
|
|
1234
|
+
// src/shared/settings.ts
|
|
1208
1235
|
const OTEL_HEADERS_HELPER = "harnessforce otel-headers";
|
|
1209
1236
|
const PLUGIN_ID$1 = "harnessforce@harnessforce-agent";
|
|
1210
1237
|
const NEW_FILE_MODE = 384;
|
|
@@ -1292,8 +1319,7 @@ async function writeUserSettings(path, settings) {
|
|
|
1292
1319
|
await rm(temporary, { force: true });
|
|
1293
1320
|
}
|
|
1294
1321
|
}
|
|
1295
|
-
|
|
1296
|
-
//#region src/destinations.ts
|
|
1322
|
+
// src/destinations.ts
|
|
1297
1323
|
const NAMES = [
|
|
1298
1324
|
"HARNESSFORCE_URL",
|
|
1299
1325
|
"HARNESSFORCE_ENDPOINT",
|
|
@@ -1314,8 +1340,7 @@ async function resolveCliDestinations(env, settingsPath, defaultUrl) {
|
|
|
1314
1340
|
readApiUrl: pick("HARNESSFORCE_URL") ?? defaultUrl
|
|
1315
1341
|
};
|
|
1316
1342
|
}
|
|
1317
|
-
|
|
1318
|
-
//#region src/client-update.ts
|
|
1343
|
+
// src/client-update.ts
|
|
1319
1344
|
const updateStatePath = (homeDir) => join(homeDir, ".harnessforce", "update-status.json");
|
|
1320
1345
|
const readCliUpdateState = (homeDir, version) => readUpdateState(updateStatePath(homeDir), version);
|
|
1321
1346
|
/**
|
|
@@ -1348,8 +1373,7 @@ async function noticeUpdate(deps) {
|
|
|
1348
1373
|
});
|
|
1349
1374
|
if (line) deps.stderr(`${line}\n`);
|
|
1350
1375
|
}
|
|
1351
|
-
|
|
1352
|
-
//#region src/help.ts
|
|
1376
|
+
// src/help.ts
|
|
1353
1377
|
const SENDS_NOTHING_LOCAL = "Nothing. It reads local files only and makes no network request.";
|
|
1354
1378
|
const COMMANDS$1 = {
|
|
1355
1379
|
init: {
|
|
@@ -1366,8 +1390,9 @@ What it does:
|
|
|
1366
1390
|
|
|
1367
1391
|
Options:
|
|
1368
1392
|
--port <port> Loopback port for the login callback, 1-65535 (default 8080).
|
|
1369
|
-
--send-content Also send the
|
|
1370
|
-
|
|
1393
|
+
--send-content Also send the prompts you type, model responses and tool
|
|
1394
|
+
input and output. Takes effect only for the categories the
|
|
1395
|
+
Workspace stores. Without it, init turns all of them off.
|
|
1371
1396
|
--no-plugin-auto-update
|
|
1372
1397
|
Turn off automatic updates of the Harnessforce plugin in
|
|
1373
1398
|
Claude Code. On by default; a value you set is kept.
|
|
@@ -1377,9 +1402,9 @@ Sends:
|
|
|
1377
1402
|
telemetry (token counts, tool names, timings) with that key.
|
|
1378
1403
|
|
|
1379
1404
|
Does not send:
|
|
1380
|
-
Prompt text
|
|
1381
|
-
|
|
1382
|
-
plain file.
|
|
1405
|
+
Prompt text, model responses and tool input or output (unless --send-content
|
|
1406
|
+
and the Workspace stores that category), code diffs, and secrets. The key is
|
|
1407
|
+
never written to a plain file.
|
|
1383
1408
|
|
|
1384
1409
|
Exit codes:
|
|
1385
1410
|
0 Connected and configured.
|
|
@@ -1469,8 +1494,8 @@ Sends:
|
|
|
1469
1494
|
The same telemetry as an agent started directly, plus the Issue identifier.
|
|
1470
1495
|
|
|
1471
1496
|
Does not send:
|
|
1472
|
-
Anything beyond what init configured. Prompt text
|
|
1473
|
-
--send-content at init.
|
|
1497
|
+
Anything beyond what init configured. Prompt text, responses and tool input
|
|
1498
|
+
and output are sent only if you chose --send-content at init.
|
|
1474
1499
|
|
|
1475
1500
|
Exit codes:
|
|
1476
1501
|
The agent's own exit code when it ran.
|
|
@@ -1561,20 +1586,26 @@ Examples:
|
|
|
1561
1586
|
`
|
|
1562
1587
|
},
|
|
1563
1588
|
hook: {
|
|
1564
|
-
summary: "Session
|
|
1565
|
-
text: `Usage: harnessforce hook session-start
|
|
1589
|
+
summary: "Session hooks (used by Codex)",
|
|
1590
|
+
text: `Usage: harnessforce hook session-start|post-tool-use|stop
|
|
1566
1591
|
|
|
1567
1592
|
When to use:
|
|
1568
|
-
You normally never run it. Codex runs
|
|
1593
|
+
You normally never run it. Codex runs session-start when a session starts,
|
|
1594
|
+
post-tool-use after it edits files with apply_patch, and stop at the end of
|
|
1595
|
+
each turn.
|
|
1569
1596
|
|
|
1570
1597
|
What it does:
|
|
1571
|
-
|
|
1572
|
-
prints context for the agent (the session ID, plus a one-line
|
|
1573
|
-
registration failed).
|
|
1598
|
+
session-start reads the hook input from stdin, registers the session with the
|
|
1599
|
+
Workspace, and prints context for the agent (the session ID, plus a one-line
|
|
1600
|
+
warning if registration failed).
|
|
1601
|
+
post-tool-use and stop record the repository and branch of the Git working
|
|
1602
|
+
trees the session worked in, and stop sends them when they changed.
|
|
1574
1603
|
|
|
1575
1604
|
Sends:
|
|
1576
1605
|
Session registration: agent, session ID, repository, branch, commit, and
|
|
1577
1606
|
start time.
|
|
1607
|
+
Work locations: agent, session ID, and the repository and branch of each
|
|
1608
|
+
working tree. Paths, commits, and file names are not sent.
|
|
1578
1609
|
|
|
1579
1610
|
Does not send:
|
|
1580
1611
|
Prompt text, model responses, tool input or output, and code diffs.
|
|
@@ -1631,8 +1662,7 @@ function resolveHelp(argv) {
|
|
|
1631
1662
|
const separator = rest.indexOf("--");
|
|
1632
1663
|
return (separator === -1 ? rest : rest.slice(0, separator)).some(isHelpFlag) ? COMMANDS$1[command].text : void 0;
|
|
1633
1664
|
}
|
|
1634
|
-
|
|
1635
|
-
//#region ../agent-core/src/hook-warning.ts
|
|
1665
|
+
// ../agent-core/src/hook-warning.ts
|
|
1636
1666
|
const RESTART_FAILED_WARNING = "Could not restart the Harnessforce hook";
|
|
1637
1667
|
const INVALID_ENDPOINT_WARNING = "The Harnessforce destination is invalid. Run `harnessforce init` again";
|
|
1638
1668
|
const notConfiguredWarning = (agent) => agent === "claude_code" ? "Harnessforce sending is not configured. Run `/harnessforce:setup`" : "Harnessforce sending is not configured. Run `harnessforce init`";
|
|
@@ -1643,21 +1673,17 @@ const DESTINATION_MISMATCH_WARNING = {
|
|
|
1643
1673
|
claude_code: "Telemetry is not being sent because the Harnessforce destination differs from the one saved by `harnessforce init`. Check this repository's settings or run `harnessforce init` again",
|
|
1644
1674
|
codex: "Sessions are not being registered because the Harnessforce destination differs from the one saved by `harnessforce init`. Run `harnessforce init` again"
|
|
1645
1675
|
};
|
|
1646
|
-
|
|
1647
|
-
//#region ../agent-core/src/issue.ts
|
|
1676
|
+
// ../agent-core/src/issue.ts
|
|
1648
1677
|
const isIssueIdentifier = (value) => isToken(value, 300);
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
//#endregion
|
|
1653
|
-
//#region ../agent-core/src/session-context.ts
|
|
1678
|
+
// ../agent-core/src/using-harnessforce.ts
|
|
1679
|
+
const usingHarnessforceBody = "---\nname: using-harnessforce\ndescription: Use when starting a session in an environment with the Harnessforce hook, when starting work on an Issue, or when asked what is recorded or collected. The hook injects this text into the context at session start.\n---\n\n# Using Harnessforce\n\nThis session has the Harnessforce hook installed. Harnessforce links the work of people and AI to Issues and records it. Follow these rules.\n\n## What is recorded\n\n- Session registration: agent, session ID, repository, branch, commit, start time, and the first prompt ID.\n- Work locations: the repository and branch of each Git working tree the session works in. Paths are not sent.\n- Config snapshot: the structure of the configuration in use (Claude Code only).\n- OTel telemetry: what the user chooses to send.\n- For work on an Issue, `record-run` registers the session as a Run and closes it. It records no Plan, Decision, or Definition of Done.\n\n## What is not collected\n\n- Prompts, responses, tool input and output, and the body of code diffs arrive only when the user sends them (`--send-content` or managed settings) and the Workspace stores that category. The members the Workspace chooses can read them. `harnessforce import` never sends them.\n- Never put these contents, secrets, or environment variable values in MCP tool input or in a record.\n\n## Which hook sends what\n\n- SessionStart: session registration and the config snapshot (Codex sends session registration only).\n- UserPromptSubmit: resends on the first prompt (Claude Code only).\n- Stop: sends the work locations at the end of a turn in which they changed.\n- A failing hook never stops the session. When the hook cannot send, it shows a one-line warning. Tell the user when a warning appears.\n\n## Which skill and tool to use, and when\n\n- For work on a specified Issue ID, or when the environment variable `HARNESSFORCE_ISSUE` is set, follow `record-run` and use `start_run`, `get_issue`, and `complete_run`. An agent that cannot load the skill calls the tools in the same order.\n- Pass the value of the `harnessforce session_id:` line in the context to `start_run`. If there are several, use the last one. If the line is missing, do not guess the session ID, and do not call `start_run` or `complete_run`.\n- Use `propose-improvements` for the analysis of `/harnessforce:tune`.\n- When no Issue is specified, do not start recording a Run.\n\n## Red Flags\n\nStop when one of these thoughts comes up.\n\n| Thought | Reality |\n| --- | --- |\n| An Issue was specified, but the work is small, so recording is unnecessary | Work on a specified Issue is recorded regardless of its size |\n| The session ID line is missing, so I will guess it and call `start_run` | Do not guess. The Run would no longer be linked to the session |\n| Summarizing the content before recording would help | Do not pass content to the tool. It goes beyond what is collected |\n| A warning appeared, but I can ignore it | Sending is not working. Tell the user |\n\n## Priority\n\n1. The user's instructions (CLAUDE.md, AGENTS.md, direct requests). If the user tells you to stop recording, stop recording the Run.\n2. This introduction.\n3. Default behavior.\n\n## Subagents\n\nA subagent that was delegated only a specific task by its parent does not register a Run by following this introduction. The parent registers the Run.\n".replace(/\r\n/g, "\n").replace(/^---\n[\s\S]*?\n---\n+/, "").trimEnd();
|
|
1680
|
+
// ../agent-core/src/session-context.ts
|
|
1654
1681
|
const sessionContextLine = (sessionId) => `harnessforce session_id: ${sessionId}`;
|
|
1655
1682
|
const sessionContext = (sessionId) => ({ hookSpecificOutput: {
|
|
1656
1683
|
hookEventName: "SessionStart",
|
|
1657
1684
|
additionalContext: `${sessionContextLine(sessionId)}\n\n${usingHarnessforceBody}`
|
|
1658
1685
|
} });
|
|
1659
|
-
|
|
1660
|
-
//#region ../agent-core/src/vcs.ts
|
|
1686
|
+
// ../agent-core/src/vcs.ts
|
|
1661
1687
|
async function selectRemote(cwd, git) {
|
|
1662
1688
|
if (await git(cwd, ["rev-parse", "--is-inside-work-tree"]) !== "true") return void 0;
|
|
1663
1689
|
const remotes = (await git(cwd, ["remote"]))?.split("\n") ?? [];
|
|
@@ -1707,8 +1733,7 @@ async function resolveProjectRoot(cwd, git) {
|
|
|
1707
1733
|
const topLevel = await git(cwd, ["rev-parse", "--show-toplevel"]);
|
|
1708
1734
|
return topLevel && isAbsolute(topLevel) ? topLevel : cwd;
|
|
1709
1735
|
}
|
|
1710
|
-
|
|
1711
|
-
//#region src/credentials/user-destination.ts
|
|
1736
|
+
// src/credentials/user-destination.ts
|
|
1712
1737
|
async function resolveUserDestination(deps) {
|
|
1713
1738
|
const { workspaceId, ingestEndpoint } = await resolveCliDestinations(deps.env, userSettingsPath(deps.env, deps.homeDir), "");
|
|
1714
1739
|
if (!workspaceId && !ingestEndpoint) return { kind: "none" };
|
|
@@ -1743,14 +1768,13 @@ async function resolveUserDestination(deps) {
|
|
|
1743
1768
|
}
|
|
1744
1769
|
};
|
|
1745
1770
|
}
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
const HOOK_USAGE = "Usage: harnessforce hook session-start\n";
|
|
1771
|
+
// src/hook/session-start.ts
|
|
1772
|
+
const HOOK_USAGE = "Usage: harnessforce hook session-start|post-tool-use|stop\n";
|
|
1749
1773
|
const MAX_INPUT_BYTES = 1048576;
|
|
1750
1774
|
const REGISTERING_SOURCES = /* @__PURE__ */ new Set(["startup", "clear"]);
|
|
1751
1775
|
const KEYCHAIN_READ_TIMEOUT_MS = 1e3;
|
|
1752
1776
|
const REVOKED_MESSAGE = "Your ingest key has been revoked. Run `harnessforce init`";
|
|
1753
|
-
function parseInput(raw) {
|
|
1777
|
+
function parseInput$1(raw) {
|
|
1754
1778
|
let fields;
|
|
1755
1779
|
try {
|
|
1756
1780
|
fields = JSON.parse(raw);
|
|
@@ -1766,7 +1790,7 @@ function parseInput(raw) {
|
|
|
1766
1790
|
source: typeof source === "string" ? source : void 0
|
|
1767
1791
|
};
|
|
1768
1792
|
}
|
|
1769
|
-
const report$
|
|
1793
|
+
const report$2 = (deps, detail) => deps.stderr(`harnessforce: session registration ${detail}\n`);
|
|
1770
1794
|
const SKIPPED_WARNINGS = {
|
|
1771
1795
|
"not configured": notConfiguredWarning("codex"),
|
|
1772
1796
|
"invalid endpoint": INVALID_ENDPOINT_WARNING,
|
|
@@ -1794,11 +1818,11 @@ async function resolveDestination(deps) {
|
|
|
1794
1818
|
const resolved = await resolveWithinLimit(deps);
|
|
1795
1819
|
if (resolved.kind === "ok") return { destination: resolved.destination };
|
|
1796
1820
|
if (resolved.kind === "none") {
|
|
1797
|
-
report$
|
|
1821
|
+
report$2(deps, "skipped (not configured)");
|
|
1798
1822
|
return { warning: notConfiguredWarning("codex") };
|
|
1799
1823
|
}
|
|
1800
1824
|
const detail = resolved.reason === "keychain unreadable" ? "no ingest key" : resolved.reason;
|
|
1801
|
-
report$
|
|
1825
|
+
report$2(deps, `skipped (${detail})`);
|
|
1802
1826
|
return { warning: SKIPPED_WARNINGS[resolved.reason] };
|
|
1803
1827
|
}
|
|
1804
1828
|
function registrationSource(env) {
|
|
@@ -1821,7 +1845,7 @@ async function register(input, deps) {
|
|
|
1821
1845
|
started_at: deps.now().toISOString()
|
|
1822
1846
|
};
|
|
1823
1847
|
const outcome = await postItem(destination, "v1/sessions", registration, deps.fetch);
|
|
1824
|
-
if (outcome.kind === "failed") report$
|
|
1848
|
+
if (outcome.kind === "failed") report$2(deps, `failed (${outcome.reason})`);
|
|
1825
1849
|
return { outcome };
|
|
1826
1850
|
}
|
|
1827
1851
|
async function selectWarning(registered, deps) {
|
|
@@ -1836,12 +1860,12 @@ async function selectWarning(registered, deps) {
|
|
|
1836
1860
|
});
|
|
1837
1861
|
}
|
|
1838
1862
|
async function sessionStart(deps) {
|
|
1839
|
-
const input = parseInput(await deps.readStdin(MAX_INPUT_BYTES).then((buffer) => buffer.toString("utf8")).catch(() => ""));
|
|
1863
|
+
const input = parseInput$1(await deps.readStdin(MAX_INPUT_BYTES).then((buffer) => buffer.toString("utf8")).catch(() => ""));
|
|
1840
1864
|
if (!input) return 0;
|
|
1841
1865
|
const registers = input.source === void 0 || REGISTERING_SOURCES.has(input.source);
|
|
1842
1866
|
const blocked = registers && await isSendingBlocked(deps);
|
|
1843
1867
|
const registered = registers && !blocked ? await register(input, deps).catch((error) => {
|
|
1844
|
-
report$
|
|
1868
|
+
report$2(deps, `failed (${error instanceof Error ? error.name : "unknown"})`);
|
|
1845
1869
|
return {};
|
|
1846
1870
|
}) : {};
|
|
1847
1871
|
const warning = registers ? await selectWarning(registered, deps) : void 0;
|
|
@@ -1852,8 +1876,185 @@ async function sessionStart(deps) {
|
|
|
1852
1876
|
})}\n`);
|
|
1853
1877
|
return 0;
|
|
1854
1878
|
}
|
|
1855
|
-
|
|
1856
|
-
|
|
1879
|
+
// ../agent-core/src/work-location.ts
|
|
1880
|
+
const absolute = (value) => typeof value === "string" && isAbsolute(value) ? value : void 0;
|
|
1881
|
+
const toolInputOf = (fields) => isObject(fields.tool_input) ? fields.tool_input : {};
|
|
1882
|
+
const PATCH_FILE_LINE = /^\*\*\* (?:Add File|Update File|Delete File|Move to): (.+)$/;
|
|
1883
|
+
function patchedDirectories(command, cwd) {
|
|
1884
|
+
if (typeof command !== "string") return [];
|
|
1885
|
+
return command.split(/\r?\n/).flatMap((line) => {
|
|
1886
|
+
const path = PATCH_FILE_LINE.exec(line)?.[1]?.trim();
|
|
1887
|
+
return path ? [dirname(resolve(cwd, path))] : [];
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* eventの入力から、作業位置を求めるdirectoryを返す。読むのは`new_cwd`、`file_path`、patchのfileの行のpathだけで、編集の
|
|
1892
|
+
* 本文を読まない。
|
|
1893
|
+
*/
|
|
1894
|
+
function observedDirectories(agent, event, fields, cwd) {
|
|
1895
|
+
if (event === "stop") return [cwd];
|
|
1896
|
+
if (agent === "claude_code" && event === "cwd-changed") {
|
|
1897
|
+
const newCwd = absolute(fields.new_cwd);
|
|
1898
|
+
return newCwd ? [newCwd] : [];
|
|
1899
|
+
}
|
|
1900
|
+
if (event !== "post-tool-use") return [];
|
|
1901
|
+
const input = toolInputOf(fields);
|
|
1902
|
+
if (agent === "codex") return fields.tool_name === "apply_patch" ? [...new Set(patchedDirectories(input.command, cwd))] : [];
|
|
1903
|
+
if (fields.tool_name !== "Write" && fields.tool_name !== "Edit") return [];
|
|
1904
|
+
const filePath = absolute(input.file_path);
|
|
1905
|
+
return filePath ? [dirname(filePath)] : [];
|
|
1906
|
+
}
|
|
1907
|
+
const isDirectory = (path) => stat(path).then((found) => found.isDirectory(), () => false);
|
|
1908
|
+
/** directoryのworking treeの、その時点のrepositoryとbranch。repositoryかbranchを求められなければundefined。 */
|
|
1909
|
+
async function observePair(directory, git) {
|
|
1910
|
+
if (!await isDirectory(directory)) return void 0;
|
|
1911
|
+
const vcs = await resolveVcs(directory, git);
|
|
1912
|
+
return vcs?.repository && vcs.branch ? {
|
|
1913
|
+
repository: vcs.repository,
|
|
1914
|
+
branch: vcs.branch
|
|
1915
|
+
} : void 0;
|
|
1916
|
+
}
|
|
1917
|
+
const emptyLocationState = () => ({
|
|
1918
|
+
pairs: [],
|
|
1919
|
+
sent: [],
|
|
1920
|
+
unauthorized: false
|
|
1921
|
+
});
|
|
1922
|
+
const pairKey = (pair) => JSON.stringify([pair.repository, pair.branch]);
|
|
1923
|
+
/** 組を加える。記録済みの組と、10件に達した後の組は加えない。 */
|
|
1924
|
+
function withObservedPair(state, pair) {
|
|
1925
|
+
if (state.pairs.some((saved) => pairKey(saved) === pairKey(pair)) || state.pairs.length >= MAX_WORK_LOCATIONS) return state;
|
|
1926
|
+
return {
|
|
1927
|
+
...state,
|
|
1928
|
+
pairs: [...state.pairs, pair]
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
/** 記録した組の集合が、最後に送った集合と異なる場合だけ送る組。 */
|
|
1932
|
+
function locationsToSend(state) {
|
|
1933
|
+
if (state.unauthorized || state.pairs.length === 0) return void 0;
|
|
1934
|
+
const sent = new Set(state.sent.map(pairKey));
|
|
1935
|
+
return sent.size === state.pairs.length && state.pairs.every((pair) => sent.has(pairKey(pair))) ? void 0 : state.pairs;
|
|
1936
|
+
}
|
|
1937
|
+
const isPair = (value) => isObject(value) && typeof value.repository === "string" && typeof value.branch === "string";
|
|
1938
|
+
async function readLocationState(path) {
|
|
1939
|
+
try {
|
|
1940
|
+
const saved = JSON.parse(await readFile(path, "utf8"));
|
|
1941
|
+
if (!isObject(saved) || !Array.isArray(saved.pairs) || !Array.isArray(saved.sent)) return emptyLocationState();
|
|
1942
|
+
return {
|
|
1943
|
+
pairs: saved.pairs.filter(isPair),
|
|
1944
|
+
sent: saved.sent.filter(isPair),
|
|
1945
|
+
unauthorized: saved.unauthorized === true
|
|
1946
|
+
};
|
|
1947
|
+
} catch {
|
|
1948
|
+
return emptyLocationState();
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
async function writeLocationState(path, state) {
|
|
1952
|
+
const temporary = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
1953
|
+
await writeFile(temporary, JSON.stringify(state), { mode: 384 });
|
|
1954
|
+
await rename(temporary, path);
|
|
1955
|
+
}
|
|
1956
|
+
const FILE_SAFE_SESSION_ID = /^[A-Za-z0-9_-]+$/;
|
|
1957
|
+
const LOCATION_DIRECTORY = "harnessforce-locations";
|
|
1958
|
+
async function isPrivateDirectory(path) {
|
|
1959
|
+
const found = await lstat(path).catch(() => void 0);
|
|
1960
|
+
if (!found) return false;
|
|
1961
|
+
const uid = process.getuid?.();
|
|
1962
|
+
return found.isDirectory() && !found.isSymbolicLink() && (uid === void 0 || found.uid === uid) && (found.mode & 511) === 448;
|
|
1963
|
+
}
|
|
1964
|
+
/**
|
|
1965
|
+
* Codexの記録のfile。Codexのhookは`scratchpad_dir`を受け取らないため、OSの一時directoryの下に置く。macOSとLinuxでは
|
|
1966
|
+
* directoryの所有者とmodeを確かめ、満たさなければundefinedを返す(記録も送信もしない)。Windowsの`TEMP`は利用者ごとに分かれる。
|
|
1967
|
+
*/
|
|
1968
|
+
async function codexLocationStatePath(temporaryDirectory, sessionId, platform = process.platform) {
|
|
1969
|
+
if (!FILE_SAFE_SESSION_ID.test(sessionId)) return void 0;
|
|
1970
|
+
const directory = join(temporaryDirectory, LOCATION_DIRECTORY);
|
|
1971
|
+
if (platform !== "win32") {
|
|
1972
|
+
await mkdir(directory, { mode: 448 }).catch(() => {});
|
|
1973
|
+
if (!await isPrivateDirectory(directory)) return void 0;
|
|
1974
|
+
} else await mkdir(directory, { recursive: true }).catch(() => {});
|
|
1975
|
+
return join(directory, `codex-${sessionId}.json`);
|
|
1976
|
+
}
|
|
1977
|
+
/** 観測したdirectoryの組を記録のfileへ加える。gitの外のdirectoryは記録しない。 */
|
|
1978
|
+
async function recordObservedPairs(statePath, directories, deps) {
|
|
1979
|
+
const pairs = await Promise.all(directories.map((directory) => observePair(directory, deps.git)));
|
|
1980
|
+
const before = await readLocationState(statePath);
|
|
1981
|
+
const after = pairs.reduce((state, pair) => pair ? withObservedPair(state, pair) : state, before);
|
|
1982
|
+
if (after !== before) await writeLocationState(statePath, after);
|
|
1983
|
+
return after;
|
|
1984
|
+
}
|
|
1985
|
+
/**
|
|
1986
|
+
* 記録した組の集合が最後に送った集合と異なれば`/v1/sessions/locations`へ送る。2xxなら送った集合を記録し、401なら以後送らない
|
|
1987
|
+
* ことを記録する。それ以外の失敗では記録を変えず、次の`Stop`が送り直しを兼ねる。
|
|
1988
|
+
*/
|
|
1989
|
+
async function sendLocations(statePath, state, item, destination, fetchImpl) {
|
|
1990
|
+
const locations = locationsToSend(state);
|
|
1991
|
+
if (!locations) return { kind: "unchanged" };
|
|
1992
|
+
const outcome = await postItem(destination, "v1/sessions/locations", {
|
|
1993
|
+
agent: item.agent,
|
|
1994
|
+
session_id: item.sessionId,
|
|
1995
|
+
locations
|
|
1996
|
+
}, fetchImpl);
|
|
1997
|
+
if (outcome.kind === "accepted") await writeLocationState(statePath, {
|
|
1998
|
+
...state,
|
|
1999
|
+
sent: locations
|
|
2000
|
+
});
|
|
2001
|
+
if (outcome.kind === "unauthorized") await writeLocationState(statePath, {
|
|
2002
|
+
...state,
|
|
2003
|
+
unauthorized: true
|
|
2004
|
+
});
|
|
2005
|
+
return {
|
|
2006
|
+
kind: "sent",
|
|
2007
|
+
outcome
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
// src/hook/work-location.ts
|
|
2011
|
+
function parseInput(raw) {
|
|
2012
|
+
let fields;
|
|
2013
|
+
try {
|
|
2014
|
+
fields = JSON.parse(raw);
|
|
2015
|
+
} catch {
|
|
2016
|
+
return;
|
|
2017
|
+
}
|
|
2018
|
+
if (!isObject(fields)) return void 0;
|
|
2019
|
+
const { session_id: sessionId, cwd } = fields;
|
|
2020
|
+
if (typeof sessionId !== "string" || !isToken(sessionId) || typeof cwd !== "string" || !isAbsolute(cwd)) return void 0;
|
|
2021
|
+
return {
|
|
2022
|
+
sessionId,
|
|
2023
|
+
cwd,
|
|
2024
|
+
fields
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
const report$1 = (deps, detail) => deps.stderr(`harnessforce: session location ${detail}\n`);
|
|
2028
|
+
async function observe(event, input, deps) {
|
|
2029
|
+
const statePath = await codexLocationStatePath(deps.hookTmpDir, input.sessionId);
|
|
2030
|
+
if (!statePath) return;
|
|
2031
|
+
const state = await recordObservedPairs(statePath, observedDirectories("codex", event, input.fields, input.cwd), deps);
|
|
2032
|
+
if (event !== "stop") return;
|
|
2033
|
+
if (state.unauthorized) return;
|
|
2034
|
+
if (await isSendingBlocked(deps)) return;
|
|
2035
|
+
const { destination } = await resolveDestination({
|
|
2036
|
+
...deps,
|
|
2037
|
+
stderr: () => {}
|
|
2038
|
+
});
|
|
2039
|
+
if (!destination) return;
|
|
2040
|
+
const sent = await sendLocations(statePath, state, {
|
|
2041
|
+
agent: "codex",
|
|
2042
|
+
sessionId: input.sessionId
|
|
2043
|
+
}, destination, deps.fetch);
|
|
2044
|
+
if (sent.kind !== "sent" || sent.outcome.kind === "accepted") return;
|
|
2045
|
+
report$1(deps, `failed (${sent.outcome.kind === "unauthorized" ? "unauthorized" : sent.outcome.reason})`);
|
|
2046
|
+
}
|
|
2047
|
+
/**
|
|
2048
|
+
* correlation.md「Codexのhook」「sessionの作業位置」。Codexの`PostToolUse`と`Stop`のhookが起動する。どの失敗でもsessionを
|
|
2049
|
+
* 止めず、常にexit 0で終える。`Stop`はstdoutにJSONを期待するため、空のobjectだけを書く。
|
|
2050
|
+
*/
|
|
2051
|
+
async function workLocationHook(event, deps) {
|
|
2052
|
+
const input = parseInput(await deps.readStdin(MAX_INPUT_BYTES).then((buffer) => buffer.toString("utf8")).catch(() => ""));
|
|
2053
|
+
if (input) await observe(event, input, deps).catch((error) => report$1(deps, `failed (${error instanceof Error ? error.name : "unknown"})`));
|
|
2054
|
+
if (event === "stop") deps.stdout("{}\n");
|
|
2055
|
+
return 0;
|
|
2056
|
+
}
|
|
2057
|
+
// src/shared/stop.ts
|
|
1857
2058
|
var CommandStop = class {
|
|
1858
2059
|
text;
|
|
1859
2060
|
constructor(text) {
|
|
@@ -1873,8 +2074,7 @@ async function runUntilStop(body, stderr) {
|
|
|
1873
2074
|
return 1;
|
|
1874
2075
|
}
|
|
1875
2076
|
}
|
|
1876
|
-
|
|
1877
|
-
//#region src/credentials/api-token.ts
|
|
2077
|
+
// src/credentials/api-token.ts
|
|
1878
2078
|
const NO_WHITESPACE$1 = /^\S+$/;
|
|
1879
2079
|
const INSTANT$2 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
|
|
1880
2080
|
const isInstant = (value) => typeof value === "string" && INSTANT$2.test(value) && !Number.isNaN(Date.parse(value));
|
|
@@ -1903,8 +2103,7 @@ function parseStoredApiToken(secret, workspaceId) {
|
|
|
1903
2103
|
return;
|
|
1904
2104
|
}
|
|
1905
2105
|
}
|
|
1906
|
-
|
|
1907
|
-
//#region src/credentials/access.ts
|
|
2106
|
+
// src/credentials/access.ts
|
|
1908
2107
|
async function verifyCliAccess(deps, messages) {
|
|
1909
2108
|
const { keychain } = deps;
|
|
1910
2109
|
const stop = (message) => stopWith(messages[message]);
|
|
@@ -1928,8 +2127,7 @@ async function verifyCliAccess(deps, messages) {
|
|
|
1928
2127
|
accessToken: apiToken.accessToken
|
|
1929
2128
|
};
|
|
1930
2129
|
}
|
|
1931
|
-
|
|
1932
|
-
//#region src/shared/http.ts
|
|
2130
|
+
// src/shared/http.ts
|
|
1933
2131
|
const REQUEST_TIMEOUT_MS = 3e4;
|
|
1934
2132
|
async function readJsonObjectBody(response) {
|
|
1935
2133
|
try {
|
|
@@ -1939,8 +2137,7 @@ async function readJsonObjectBody(response) {
|
|
|
1939
2137
|
return;
|
|
1940
2138
|
}
|
|
1941
2139
|
}
|
|
1942
|
-
|
|
1943
|
-
//#region src/shared/metadata.ts
|
|
2140
|
+
// src/shared/metadata.ts
|
|
1944
2141
|
const WELL_KNOWN = "/.well-known/oauth-authorization-server";
|
|
1945
2142
|
const withoutTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
1946
2143
|
async function fetchMetadata(base, fetchImpl) {
|
|
@@ -1975,8 +2172,7 @@ async function discoverTokenEndpoint(base, fetchImpl) {
|
|
|
1975
2172
|
const endpoint = endpointOf(await fetchMetadata(base, fetchImpl), "token_endpoint");
|
|
1976
2173
|
return endpoint?.origin === base.origin ? endpoint : void 0;
|
|
1977
2174
|
}
|
|
1978
|
-
|
|
1979
|
-
//#region src/credentials/token-lock.ts
|
|
2175
|
+
// src/credentials/token-lock.ts
|
|
1980
2176
|
const tokenLockPath = (homeDir) => join(homeDir, ".harnessforce", "token.lock");
|
|
1981
2177
|
const LOCK_WAIT_MS$1 = 3e4;
|
|
1982
2178
|
const STALE_LOCK_MS$1 = 6e4;
|
|
@@ -2006,8 +2202,7 @@ async function acquireTokenLock(path, deps) {
|
|
|
2006
2202
|
await deps.sleep(RETRY_INTERVAL_MS$1);
|
|
2007
2203
|
}
|
|
2008
2204
|
}
|
|
2009
|
-
|
|
2010
|
-
//#region src/credentials/api-token-session.ts
|
|
2205
|
+
// src/credentials/api-token-session.ts
|
|
2011
2206
|
const CLIENT_ID$1 = "harnessforce-cli";
|
|
2012
2207
|
const LOCKED_KEYCHAIN_TIMEOUT_MS = 1e4;
|
|
2013
2208
|
const isPast = (instant, now) => now >= Date.parse(instant);
|
|
@@ -2117,8 +2312,7 @@ function createApiTokenSession(deps) {
|
|
|
2117
2312
|
authorizedFetch
|
|
2118
2313
|
};
|
|
2119
2314
|
}
|
|
2120
|
-
|
|
2121
|
-
//#region src/import/read-api.ts
|
|
2315
|
+
// src/import/read-api.ts
|
|
2122
2316
|
const FAILED$1 = { kind: "failed" };
|
|
2123
2317
|
async function getJson(url, apiToken, fetchImpl) {
|
|
2124
2318
|
try {
|
|
@@ -2188,8 +2382,7 @@ async function fetchSessionImportDays(base, apiToken, fetchImpl) {
|
|
|
2188
2382
|
value: days
|
|
2189
2383
|
} : FAILED$1;
|
|
2190
2384
|
}
|
|
2191
|
-
|
|
2192
|
-
//#region src/import/record-base.ts
|
|
2385
|
+
// src/import/record-base.ts
|
|
2193
2386
|
async function readSettingsEnv(path) {
|
|
2194
2387
|
const read = await readUserSettings(path);
|
|
2195
2388
|
const env = read.kind === "ok" ? read.settings.env : void 0;
|
|
@@ -2210,8 +2403,7 @@ async function recordBase(deps) {
|
|
|
2210
2403
|
const { CLAUDE_CONFIG_DIR: configDir } = await readTrustedEnv(deps, ["CLAUDE_CONFIG_DIR"]);
|
|
2211
2404
|
return configDir ?? join(deps.homeDir, ".claude");
|
|
2212
2405
|
}
|
|
2213
|
-
|
|
2214
|
-
//#region src/import/state.ts
|
|
2406
|
+
// src/import/state.ts
|
|
2215
2407
|
const STATE_VERSION = 1;
|
|
2216
2408
|
const DIR_MODE$1 = 448;
|
|
2217
2409
|
const FILE_MODE$3 = 384;
|
|
@@ -2268,8 +2460,7 @@ async function recordSentSessions(path, destination, sessionIds) {
|
|
|
2268
2460
|
await rm(temporary, { force: true });
|
|
2269
2461
|
}
|
|
2270
2462
|
}
|
|
2271
|
-
|
|
2272
|
-
//#region src/import/send.ts
|
|
2463
|
+
// src/import/send.ts
|
|
2273
2464
|
const BATCH_SIZE$1 = 100;
|
|
2274
2465
|
const MAX_RESENDS = 3;
|
|
2275
2466
|
const MAX_RETRY_AFTER_SECONDS = 60;
|
|
@@ -2486,8 +2677,7 @@ var SessionAccumulator = class {
|
|
|
2486
2677
|
};
|
|
2487
2678
|
}
|
|
2488
2679
|
};
|
|
2489
|
-
|
|
2490
|
-
//#region src/import/rollout.ts
|
|
2680
|
+
// src/import/rollout.ts
|
|
2491
2681
|
const CODEX_PARSER_VERSION = "1.0.0";
|
|
2492
2682
|
const DETACHED$2 = "HEAD";
|
|
2493
2683
|
const COMMIT_SHA = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
@@ -2624,8 +2814,7 @@ async function parseRollout(path) {
|
|
|
2624
2814
|
}
|
|
2625
2815
|
return accumulator.result(skippedLines);
|
|
2626
2816
|
}
|
|
2627
|
-
|
|
2628
|
-
//#region src/import/transcript.ts
|
|
2817
|
+
// src/import/transcript.ts
|
|
2629
2818
|
const PARSER_VERSION = "1.1.0";
|
|
2630
2819
|
const isToken$1 = (value, maxLength = 256) => typeof value === "string" && value.length >= 1 && value.length <= maxLength && /^\S+$/.test(value);
|
|
2631
2820
|
const MAX_TOOL_NAME = 128;
|
|
@@ -2902,8 +3091,7 @@ async function parseTranscriptUsage(path) {
|
|
|
2902
3091
|
const { usage, isReadable } = await readTranscript(path, new Accumulator());
|
|
2903
3092
|
return isReadable ? usage : void 0;
|
|
2904
3093
|
}
|
|
2905
|
-
|
|
2906
|
-
//#region src/import/sessions.ts
|
|
3094
|
+
// src/import/sessions.ts
|
|
2907
3095
|
const noneNotImported = () => ({
|
|
2908
3096
|
outOfRange: 0,
|
|
2909
3097
|
outsideGit: 0,
|
|
@@ -3064,8 +3252,7 @@ async function scanCodexSessions(options) {
|
|
|
3064
3252
|
compressed
|
|
3065
3253
|
};
|
|
3066
3254
|
}
|
|
3067
|
-
|
|
3068
|
-
//#region src/import/command.ts
|
|
3255
|
+
// src/import/command.ts
|
|
3069
3256
|
const DAY_MS$1 = 864e5;
|
|
3070
3257
|
const MESSAGES = {
|
|
3071
3258
|
runInit: "Run `harnessforce init`",
|
|
@@ -3195,9 +3382,8 @@ async function runImport(deps) {
|
|
|
3195
3382
|
function importCommand(deps) {
|
|
3196
3383
|
return runUntilStop(() => runImport(deps), deps.stderr);
|
|
3197
3384
|
}
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
//! @decimalturn/toml-patch v3.3.0 - https://github.com/DecimalTurn/toml-patch - @license: MIT
|
|
3385
|
+
// ../../node_modules/.pnpm/@decimalturn+toml-patch@3.3.1/node_modules/@decimalturn/toml-patch/dist/toml-patch.js
|
|
3386
|
+
//! @decimalturn/toml-patch v3.3.1 - https://github.com/DecimalTurn/toml-patch - @license: MIT
|
|
3201
3387
|
function e(e) {
|
|
3202
3388
|
return e.type === `Document`;
|
|
3203
3389
|
}
|
|
@@ -4573,13 +4759,12 @@ function on(e) {
|
|
|
4573
4759
|
if (e[t] !== `\\`) continue;
|
|
4574
4760
|
let n = e[t + 1];
|
|
4575
4761
|
if (!n) return null;
|
|
4576
|
-
if (n === `\\`)
|
|
4577
|
-
|
|
4578
|
-
|
|
4762
|
+
if (n === `\\`) t++;
|
|
4763
|
+
else {
|
|
4764
|
+
if (n === `x` && /^[0-9A-Fa-f]{2}$/.test(e.slice(t + 2, t + 4))) return /[A-F]/.test(e.slice(t + 2, t + 4));
|
|
4765
|
+
if (n === `u` && /^[0-9A-Fa-f]{4}$/.test(e.slice(t + 2, t + 6))) return /[A-F]/.test(e.slice(t + 2, t + 6));
|
|
4766
|
+
if (n === `U` && /^[0-9A-Fa-f]{8}$/.test(e.slice(t + 2, t + 10))) return /[A-F]/.test(e.slice(t + 2, t + 10));
|
|
4579
4767
|
}
|
|
4580
|
-
if (n === `x` && /^[0-9A-Fa-f]{2}$/.test(e.slice(t + 2, t + 4))) return /[A-F]/.test(e.slice(t + 2, t + 4));
|
|
4581
|
-
if (n === `u` && /^[0-9A-Fa-f]{4}$/.test(e.slice(t + 2, t + 6))) return /[A-F]/.test(e.slice(t + 2, t + 6));
|
|
4582
|
-
if (n === `U` && /^[0-9A-Fa-f]{8}$/.test(e.slice(t + 2, t + 10))) return /[A-F]/.test(e.slice(t + 2, t + 10));
|
|
4583
4768
|
}
|
|
4584
4769
|
return null;
|
|
4585
4770
|
}
|
|
@@ -4891,14 +5076,11 @@ function qn(e, t) {
|
|
|
4891
5076
|
if (e === t) return r;
|
|
4892
5077
|
if (a(e)) return n(e.value, r);
|
|
4893
5078
|
if (d(e)) return n(e.item, r);
|
|
4894
|
-
if (u(e) || f(e)) {
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
if (e !== void 0) return e;
|
|
4898
|
-
}
|
|
4899
|
-
return;
|
|
5079
|
+
if (u(e) || f(e)) for (let t of e.items) {
|
|
5080
|
+
let e = n(t, r + 1);
|
|
5081
|
+
if (e !== void 0) return e;
|
|
4900
5082
|
}
|
|
4901
|
-
if (m(e)) for (let t of e.items) {
|
|
5083
|
+
else if (m(e)) for (let t of e.items) {
|
|
4902
5084
|
let e = n(t, r);
|
|
4903
5085
|
if (e !== void 0) return e;
|
|
4904
5086
|
}
|
|
@@ -4910,14 +5092,11 @@ function Jn(e, t) {
|
|
|
4910
5092
|
if (e === t) return r;
|
|
4911
5093
|
if (a(e)) return n(e.value, r);
|
|
4912
5094
|
if (d(e)) return n(e.item, r);
|
|
4913
|
-
if (u(e) || f(e)) {
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
if (r) return r;
|
|
4917
|
-
}
|
|
4918
|
-
return;
|
|
5095
|
+
if (u(e) || f(e)) for (let t of e.items) {
|
|
5096
|
+
let r = n(t, e);
|
|
5097
|
+
if (r) return r;
|
|
4919
5098
|
}
|
|
4920
|
-
if (m(e)) for (let t of e.items) {
|
|
5099
|
+
else if (m(e)) for (let t of e.items) {
|
|
4921
5100
|
let e = n(t, r);
|
|
4922
5101
|
if (e) return e;
|
|
4923
5102
|
}
|
|
@@ -5617,25 +5796,17 @@ function Nr(e) {
|
|
|
5617
5796
|
let r = e[n + 1];
|
|
5618
5797
|
if (!r) continue;
|
|
5619
5798
|
let i = kr(r);
|
|
5620
|
-
if (i !== null)
|
|
5621
|
-
i !== `
|
|
5799
|
+
if (i !== null) i !== `
|
|
5622
5800
|
` && i !== `\r` && !t.has(i) && t.set(i, `\\` + r), n += 1;
|
|
5623
|
-
|
|
5624
|
-
}
|
|
5625
|
-
if (r === `x` && /^[0-9A-Fa-f]{2}$/.test(e.slice(n + 2, n + 4))) {
|
|
5801
|
+
else if (r === `x` && /^[0-9A-Fa-f]{2}$/.test(e.slice(n + 2, n + 4))) {
|
|
5626
5802
|
let r = e.slice(n, n + 4), i = Number.parseInt(e.slice(n + 2, n + 4), 16), a = String.fromCharCode(i);
|
|
5627
5803
|
t.has(a) || t.set(a, r), n += 3;
|
|
5628
|
-
|
|
5629
|
-
}
|
|
5630
|
-
if (r === `u` && /^[0-9A-Fa-f]{4}$/.test(e.slice(n + 2, n + 6))) {
|
|
5804
|
+
} else if (r === `u` && /^[0-9A-Fa-f]{4}$/.test(e.slice(n + 2, n + 6))) {
|
|
5631
5805
|
let r = e.slice(n, n + 6), i = Number.parseInt(e.slice(n + 2, n + 6), 16), a = String.fromCodePoint(i);
|
|
5632
5806
|
t.has(a) || t.set(a, r), n += 5;
|
|
5633
|
-
|
|
5634
|
-
}
|
|
5635
|
-
if (r === `U` && /^[0-9A-Fa-f]{8}$/.test(e.slice(n + 2, n + 10))) {
|
|
5807
|
+
} else if (r === `U` && /^[0-9A-Fa-f]{8}$/.test(e.slice(n + 2, n + 10))) {
|
|
5636
5808
|
let r = e.slice(n, n + 10), i = Number.parseInt(e.slice(n + 2, n + 10), 16), a = String.fromCodePoint(i);
|
|
5637
5809
|
t.has(a) || t.set(a, r), n += 9;
|
|
5638
|
-
continue;
|
|
5639
5810
|
}
|
|
5640
5811
|
}
|
|
5641
5812
|
return t;
|
|
@@ -5644,11 +5815,7 @@ function Pr(e, t, n, r) {
|
|
|
5644
5815
|
let i = ``;
|
|
5645
5816
|
for (let a of e) {
|
|
5646
5817
|
let e = t.get(a);
|
|
5647
|
-
|
|
5648
|
-
i += e;
|
|
5649
|
-
continue;
|
|
5650
|
-
}
|
|
5651
|
-
i += jr(a, n, r);
|
|
5818
|
+
i += e || jr(a, n, r);
|
|
5652
5819
|
}
|
|
5653
5820
|
return i;
|
|
5654
5821
|
}
|
|
@@ -6052,7 +6219,7 @@ function oi(e) {
|
|
|
6052
6219
|
return !f(e) || e.items.length === 0 ? !1 : e.items[e.items.length - 1].comma === !0;
|
|
6053
6220
|
}
|
|
6054
6221
|
function si(e, t) {
|
|
6055
|
-
return t.inlineTableStart === 0
|
|
6222
|
+
return t.inlineTableStart === 0 || (e.items.filter((e) => {
|
|
6056
6223
|
if (!a(e)) return !1;
|
|
6057
6224
|
let n = f(e.value), r = u(e.value) && e.value.items.length > 0 && e.value.items.every((e) => f(e.item));
|
|
6058
6225
|
if (n || r) {
|
|
@@ -6064,7 +6231,7 @@ function si(e, t) {
|
|
|
6064
6231
|
V(e, e, n), f(n.value) ? n.value.items.length === 0 ? B(e, e, W(n.key.value)) : B(e, e, ci(n, t.bracketSpacing, t.escapeSequenceUpperCase)) : li(n, t.bracketSpacing, t.escapeSequenceUpperCase).forEach((t) => {
|
|
6065
6232
|
B(e, e, t);
|
|
6066
6233
|
});
|
|
6067
|
-
}), H(e), e
|
|
6234
|
+
}), H(e)), e;
|
|
6068
6235
|
}
|
|
6069
6236
|
function ci(e, t, n) {
|
|
6070
6237
|
let r = W(e.key.value, n);
|
|
@@ -7101,11 +7268,7 @@ function ia(e, t, n = [], r = {}) {
|
|
|
7101
7268
|
let h = 0;
|
|
7102
7269
|
for (let t = e; t < o.length; t++) o[t] === r && h++;
|
|
7103
7270
|
let g = p > h;
|
|
7104
|
-
if (s && (o.indexOf(a[e]) === -1 || g)) {
|
|
7105
|
-
i.push(d(e + l)), a.splice(e, 1), c.splice(e, 1), l++, e--;
|
|
7106
|
-
continue;
|
|
7107
|
-
}
|
|
7108
|
-
if (l > 0 && g) {
|
|
7271
|
+
if (s && (o.indexOf(a[e]) === -1 || g) || l > 0 && g) {
|
|
7109
7272
|
i.push(d(e + l)), a.splice(e, 1), c.splice(e, 1), l++, e--;
|
|
7110
7273
|
continue;
|
|
7111
7274
|
}
|
|
@@ -7136,14 +7299,10 @@ function ia(e, t, n = [], r = {}) {
|
|
|
7136
7299
|
for (let n = e; n < o.length; n++) o[n] === t && r++;
|
|
7137
7300
|
h = n > r;
|
|
7138
7301
|
}
|
|
7139
|
-
|
|
7140
|
-
Vt(i, $i(c[e], t[e], n.concat(e), r)), a[e] = f, c[e] = t[e];
|
|
7141
|
-
continue;
|
|
7142
|
-
}
|
|
7143
|
-
i.push({
|
|
7302
|
+
!p && h ? (Vt(i, $i(c[e], t[e], n.concat(e), r)), a[e] = f, c[e] = t[e]) : (i.push({
|
|
7144
7303
|
type: `Add`,
|
|
7145
7304
|
path: n.concat(e)
|
|
7146
|
-
}), u = !0, a.splice(e, 0, f), c.splice(e, 0, t[e]), l > 0 && l
|
|
7305
|
+
}), u = !0, a.splice(e, 0, f), c.splice(e, 0, t[e]), l > 0 && l--);
|
|
7147
7306
|
}
|
|
7148
7307
|
for (let e = o.length; e < a.length; e++) i.push(d(e + l));
|
|
7149
7308
|
return i;
|
|
@@ -7229,19 +7388,10 @@ function pa(e) {
|
|
|
7229
7388
|
let n = !1, r = !1;
|
|
7230
7389
|
for (let i = t + 1; i < e.raw.length; i++) {
|
|
7231
7390
|
let t = e.raw[i];
|
|
7232
|
-
if (t === `\\`)
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
if (!r && t === `"`) {
|
|
7237
|
-
n = !n;
|
|
7238
|
-
continue;
|
|
7239
|
-
}
|
|
7240
|
-
if (!n && t === `'`) {
|
|
7241
|
-
r = !r;
|
|
7242
|
-
continue;
|
|
7243
|
-
}
|
|
7244
|
-
if (!n && !r && t === `#`) return !0;
|
|
7391
|
+
if (t === `\\`) i++;
|
|
7392
|
+
else if (!r && t === `"`) n = !n;
|
|
7393
|
+
else if (!n && t === `'`) r = !r;
|
|
7394
|
+
else if (!n && !r && t === `#`) return !0;
|
|
7245
7395
|
}
|
|
7246
7396
|
return !1;
|
|
7247
7397
|
}
|
|
@@ -7509,13 +7659,7 @@ function Ta(t, n, r, i) {
|
|
|
7509
7659
|
e && (e.lines = 0, e.columns = 0);
|
|
7510
7660
|
}
|
|
7511
7661
|
H(t), p && h && r.loc.end.line > r.loc.start.line && (r.loc.end.column = h.column, d(r) && g && r.item.loc.end.line > r.item.loc.start.line && (r.item.loc.end.column = g.column));
|
|
7512
|
-
for (let { node: e, start: t, end: n, endOnly: r, equals: i } of _)
|
|
7513
|
-
if (r) {
|
|
7514
|
-
e.loc.end.column = n.column;
|
|
7515
|
-
continue;
|
|
7516
|
-
}
|
|
7517
|
-
e.loc.start.column = t.column, e.loc.end.line === e.loc.start.line && (e.loc.end.column = n.column), i !== void 0 && a(e) && (e.equals = i);
|
|
7518
|
-
}
|
|
7662
|
+
for (let { node: e, start: t, end: n, endOnly: r, equals: i } of _) r ? e.loc.end.column = n.column : (e.loc.start.column = t.column, e.loc.end.line === e.loc.start.line && (e.loc.end.column = n.column), i !== void 0 && a(e) && (e.equals = i));
|
|
7519
7663
|
for (let { owner: t, ownerOriginalStart: n, comments: i } of l) {
|
|
7520
7664
|
let a;
|
|
7521
7665
|
t === r ? (a = {
|
|
@@ -7640,11 +7784,7 @@ function Ea(e) {
|
|
|
7640
7784
|
continue;
|
|
7641
7785
|
}
|
|
7642
7786
|
let e = t(i.member) || r(i.member), a = n[n.length - 1];
|
|
7643
|
-
|
|
7644
|
-
a.groups.push(i), a.endLine = i.endLine, a.items = a.items.concat(i.items);
|
|
7645
|
-
continue;
|
|
7646
|
-
}
|
|
7647
|
-
n.push({
|
|
7787
|
+
a && a.kind === `member` && i.key !== void 0 && a.key === i.key ? (a.groups.push(i), a.endLine = i.endLine, a.items = a.items.concat(i.items)) : n.push({
|
|
7648
7788
|
kind: `member`,
|
|
7649
7789
|
key: i.key,
|
|
7650
7790
|
isSection: e,
|
|
@@ -7869,23 +8009,11 @@ function Ua(e, t, n) {
|
|
|
7869
8009
|
return r.columns + (i || a ? 2 : 0) + (s ? 1 + (c ? -1 : 0) : 0);
|
|
7870
8010
|
}
|
|
7871
8011
|
function Wa(e, n = []) {
|
|
7872
|
-
for (let i of e.items)
|
|
7873
|
-
if (p(i)) {
|
|
7874
|
-
n.push(i);
|
|
7875
|
-
continue;
|
|
7876
|
-
}
|
|
7877
|
-
(t(i) || r(i)) && Wa(i, n);
|
|
7878
|
-
}
|
|
8012
|
+
for (let i of e.items) p(i) ? n.push(i) : (t(i) || r(i)) && Wa(i, n);
|
|
7879
8013
|
return n;
|
|
7880
8014
|
}
|
|
7881
8015
|
function Ga(e, n = /* @__PURE__ */ new Set()) {
|
|
7882
|
-
for (let i of e.items)
|
|
7883
|
-
if (a(i) && f(i.value) && i.loc.start.line === i.loc.end.line) {
|
|
7884
|
-
n.add(i.loc.end.line);
|
|
7885
|
-
continue;
|
|
7886
|
-
}
|
|
7887
|
-
(t(i) || r(i)) && Ga(i, n);
|
|
7888
|
-
}
|
|
8016
|
+
for (let i of e.items) a(i) && f(i.value) && i.loc.start.line === i.loc.end.line ? n.add(i.loc.end.line) : (t(i) || r(i)) && Ga(i, n);
|
|
7889
8017
|
return n;
|
|
7890
8018
|
}
|
|
7891
8019
|
function Ka(e) {
|
|
@@ -8257,11 +8385,7 @@ function so(e) {
|
|
|
8257
8385
|
let t = [], n = 0, r, i = !1;
|
|
8258
8386
|
for (let a = 0; a < e.length; a++) {
|
|
8259
8387
|
let o = e[a];
|
|
8260
|
-
|
|
8261
|
-
r === `"` && i ? i = !1 : r === `"` && o === `\\` ? i = !0 : o === r && (r = void 0);
|
|
8262
|
-
continue;
|
|
8263
|
-
}
|
|
8264
|
-
o === `"` || o === `'` ? r = o : o === `.` && (t.push(e.slice(n, a)), n = a + 1);
|
|
8388
|
+
r ? r === `"` && i ? i = !1 : r === `"` && o === `\\` ? i = !0 : o === r && (r = void 0) : o === `"` || o === `'` ? r = o : o === `.` && (t.push(e.slice(n, a)), n = a + 1);
|
|
8265
8389
|
}
|
|
8266
8390
|
return t.push(e.slice(n)), t;
|
|
8267
8391
|
}
|
|
@@ -9013,11 +9137,20 @@ function yo(n, i, s, c, l = !1, g = /* @__PURE__ */ new WeakSet(), _ = void 0, v
|
|
|
9013
9137
|
for (let e of s) Q(n, l, e);
|
|
9014
9138
|
}
|
|
9015
9139
|
if ((t(o) || r(o)) && m(l)) {
|
|
9016
|
-
let e = o.key.item.value, i = l.items, s = [];
|
|
9017
|
-
for (let
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9140
|
+
let e = o.key.item.value, i = l.items, s = [], c = r(o), u = i.indexOf(o), d = i.length;
|
|
9141
|
+
if (c) for (let t = u + 1; t < i.length; t++) {
|
|
9142
|
+
let n = i[t];
|
|
9143
|
+
if (!r(n)) continue;
|
|
9144
|
+
let a = n.key.item.value;
|
|
9145
|
+
if (a.length <= e.length && F(a, e.slice(0, a.length))) {
|
|
9146
|
+
d = t;
|
|
9147
|
+
break;
|
|
9148
|
+
}
|
|
9149
|
+
}
|
|
9150
|
+
for (let [n, l] of i.entries()) {
|
|
9151
|
+
if (l === o || c && (n < u || n >= d)) continue;
|
|
9152
|
+
let i = a(l) ? l.key.value : t(l) || r(l) ? l.key.item.value : void 0;
|
|
9153
|
+
i && i.length > e.length && F(i.slice(0, e.length), e) && s.push(l);
|
|
9021
9154
|
}
|
|
9022
9155
|
for (let e of s) Q(n, l, e);
|
|
9023
9156
|
}
|
|
@@ -9403,23 +9536,18 @@ function To(e, n, i, o, s, c, l) {
|
|
|
9403
9536
|
let n = s.items.find((e) => a(e) && F(e.key.value, t.key.value));
|
|
9404
9537
|
n ? pr(e, s, n, t) : B(e, s, t, void 0);
|
|
9405
9538
|
};
|
|
9406
|
-
for (let e of m)
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
n.key.loc.end.column = n.key.loc.start.column + n.key.raw.length, n.equals += i, U(n.value, {
|
|
9419
|
-
lines: 0,
|
|
9420
|
-
columns: i
|
|
9421
|
-
}, { first_line_only: !0 }), n.loc.end.line === n.loc.start.line && (n.loc.end.column += i), h(n);
|
|
9422
|
-
}
|
|
9539
|
+
for (let e of m) if (a(e)) h(e);
|
|
9540
|
+
else if (t(e)) {
|
|
9541
|
+
let t = e.key.item.value;
|
|
9542
|
+
for (let n of e.items) {
|
|
9543
|
+
if (!a(n)) continue;
|
|
9544
|
+
let e = n.key.raw, r = t.concat(n.key.value);
|
|
9545
|
+
n.key.value = r, n.key.raw = Ur(r, o.escapeSequenceUpperCase).raw;
|
|
9546
|
+
let i = n.key.raw.length - e.length;
|
|
9547
|
+
n.key.loc.end.column = n.key.loc.start.column + n.key.raw.length, n.equals += i, U(n.value, {
|
|
9548
|
+
lines: 0,
|
|
9549
|
+
columns: i
|
|
9550
|
+
}, { first_line_only: !0 }), n.loc.end.line === n.loc.start.line && (n.loc.end.column += i), h(n);
|
|
9423
9551
|
}
|
|
9424
9552
|
}
|
|
9425
9553
|
return;
|
|
@@ -9438,51 +9566,51 @@ function To(e, n, i, o, s, c, l) {
|
|
|
9438
9566
|
if (o) {
|
|
9439
9567
|
for (let t of i.items) B(e, o, t, void 0);
|
|
9440
9568
|
v ||= o;
|
|
9441
|
-
|
|
9442
|
-
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
}
|
|
9569
|
+
} else {
|
|
9570
|
+
if (a(i) && f(i.value)) {
|
|
9571
|
+
let o = i.key.value, s = e.items.find((e) => t(e) && F(e.key.item.value, o));
|
|
9572
|
+
if (s) {
|
|
9573
|
+
for (let t of i.value.items) B(e, s, d(t) ? t.item : t, void 0);
|
|
9574
|
+
v ||= s;
|
|
9575
|
+
continue;
|
|
9576
|
+
}
|
|
9577
|
+
if ($(e, o).filter((e) => a(e) || t(e) || r(e)).length > 0) {
|
|
9578
|
+
for (let t of i.value.items) {
|
|
9579
|
+
let r = d(t) ? t.item : t;
|
|
9580
|
+
if (a(r)) {
|
|
9581
|
+
let e = r.key.raw, t = o.concat(r.key.value);
|
|
9582
|
+
r.key.value = t, r.key.raw = t.map((e) => A.test(e) ? e : JSON.stringify(e).replace(/\x7f/g, `\\u007f`)).join(`.`);
|
|
9583
|
+
let n = r.key.raw.length - e.length;
|
|
9584
|
+
r.key.loc.end.column = r.key.loc.start.column + r.key.raw.length, r.equals += n, U(r.value, {
|
|
9585
|
+
lines: 0,
|
|
9586
|
+
columns: n
|
|
9587
|
+
}, { first_line_only: !0 }), r.loc.end.line === r.loc.start.line && (r.loc.end.column += n);
|
|
9588
|
+
}
|
|
9589
|
+
B(e, e, r, n === 0 ? _ : void 0), v ||= r;
|
|
9461
9590
|
}
|
|
9462
|
-
|
|
9591
|
+
continue;
|
|
9463
9592
|
}
|
|
9464
|
-
continue;
|
|
9465
9593
|
}
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
}
|
|
9594
|
+
if (t(i)) {
|
|
9595
|
+
let t = i.key.item.value;
|
|
9596
|
+
if ($(e, t).filter((e) => a(e)).length > 0) {
|
|
9597
|
+
for (let r of i.items) {
|
|
9598
|
+
if (a(r)) {
|
|
9599
|
+
let e = r.key.raw, n = t.concat(r.key.value);
|
|
9600
|
+
r.key.value = n, r.key.raw = n.map((e) => A.test(e) ? e : JSON.stringify(e).replace(/\x7f/g, `\\u007f`)).join(`.`);
|
|
9601
|
+
let i = r.key.raw.length - e.length;
|
|
9602
|
+
r.key.loc.end.column = r.key.loc.start.column + r.key.raw.length, r.equals += i, U(r.value, {
|
|
9603
|
+
lines: 0,
|
|
9604
|
+
columns: i
|
|
9605
|
+
}, { first_line_only: !0 }), r.loc.end.line === r.loc.start.line && (r.loc.end.column += i);
|
|
9606
|
+
}
|
|
9607
|
+
B(e, e, r, n === 0 ? _ : void 0), v ||= r;
|
|
9479
9608
|
}
|
|
9480
|
-
|
|
9609
|
+
continue;
|
|
9481
9610
|
}
|
|
9482
|
-
continue;
|
|
9483
9611
|
}
|
|
9612
|
+
n > 0 && v && t(v) && a(i) ? B(e, v, i, void 0) : B(e, e, i, n === 0 ? _ : void 0), v ||= i;
|
|
9484
9613
|
}
|
|
9485
|
-
n > 0 && v && t(v) && a(i) ? B(e, v, i, void 0) : B(e, e, i, n === 0 ? _ : void 0), v ||= i;
|
|
9486
9614
|
}
|
|
9487
9615
|
let y = v;
|
|
9488
9616
|
t(y) && l.add(y), c.add(y);
|
|
@@ -9665,11 +9793,26 @@ function Ko(e, t) {
|
|
|
9665
9793
|
temporal: (t == null ? void 0 : t.temporal) ?? !1
|
|
9666
9794
|
});
|
|
9667
9795
|
}
|
|
9668
|
-
|
|
9669
|
-
//#region src/shared/codex-config.ts
|
|
9670
|
-
const HOOK_COMMAND = "harnessforce hook session-start";
|
|
9671
|
-
const HOOK_COMMAND_WINDOWS = "harnessforce.cmd hook session-start";
|
|
9796
|
+
// src/shared/codex-config.ts
|
|
9672
9797
|
const HOOK_TIMEOUT_SECONDS = 10;
|
|
9798
|
+
const HOOKS = [
|
|
9799
|
+
{
|
|
9800
|
+
event: "SessionStart",
|
|
9801
|
+
subcommand: "session-start"
|
|
9802
|
+
},
|
|
9803
|
+
{
|
|
9804
|
+
event: "PostToolUse",
|
|
9805
|
+
subcommand: "post-tool-use",
|
|
9806
|
+
matcher: "^apply_patch$",
|
|
9807
|
+
isAsync: true
|
|
9808
|
+
},
|
|
9809
|
+
{
|
|
9810
|
+
event: "Stop",
|
|
9811
|
+
subcommand: "stop",
|
|
9812
|
+
isAsync: true
|
|
9813
|
+
}
|
|
9814
|
+
];
|
|
9815
|
+
const hookCommand = (spec) => `harnessforce hook ${spec.subcommand}`;
|
|
9673
9816
|
const FILE_MODE$2 = 384;
|
|
9674
9817
|
function codexConfigPath(env, homeDir) {
|
|
9675
9818
|
return join(absoluteEnv(env.CODEX_HOME) ?? join(homeDir, ".codex"), "config.toml");
|
|
@@ -9705,11 +9848,14 @@ function withHarnessforce(config, input) {
|
|
|
9705
9848
|
headers: { Authorization: `Bearer ${input.ingestKey}` },
|
|
9706
9849
|
protocol: "binary"
|
|
9707
9850
|
} };
|
|
9708
|
-
|
|
9851
|
+
otel.log_user_prompt = input.logUserPrompt;
|
|
9852
|
+
otel.log_agent_responses = input.logAgentResponses;
|
|
9709
9853
|
const hooks = tableAt(config, "hooks");
|
|
9710
|
-
const
|
|
9711
|
-
|
|
9712
|
-
|
|
9854
|
+
for (const spec of HOOKS) {
|
|
9855
|
+
const groups = hooks[spec.event] ?? [];
|
|
9856
|
+
if (!Array.isArray(groups)) throw new Error(spec.event);
|
|
9857
|
+
hooks[spec.event] = withHandler(groups, spec);
|
|
9858
|
+
}
|
|
9713
9859
|
const servers = tableAt(config, "mcp_servers");
|
|
9714
9860
|
const existing = servers.harnessforce;
|
|
9715
9861
|
if (existing !== void 0 && !isObject(existing)) throw new Error("server");
|
|
@@ -9725,16 +9871,21 @@ function tableAt(parent, key) {
|
|
|
9725
9871
|
parent[key] = value;
|
|
9726
9872
|
return value;
|
|
9727
9873
|
}
|
|
9728
|
-
|
|
9729
|
-
const
|
|
9730
|
-
|
|
9874
|
+
function withHandler(groups, spec) {
|
|
9875
|
+
const command = hookCommand(spec);
|
|
9876
|
+
const isOurs = (entry) => isObject(entry) && entry.command === command;
|
|
9877
|
+
const hasOurs = (group) => isObject(group) && Array.isArray(group.hooks) && group.hooks.some(isOurs);
|
|
9731
9878
|
const handler = {
|
|
9732
9879
|
type: "command",
|
|
9733
|
-
command
|
|
9734
|
-
commandWindows:
|
|
9735
|
-
timeout: HOOK_TIMEOUT_SECONDS
|
|
9736
|
-
|
|
9737
|
-
|
|
9880
|
+
command,
|
|
9881
|
+
commandWindows: `harnessforce.cmd hook ${spec.subcommand}`,
|
|
9882
|
+
timeout: HOOK_TIMEOUT_SECONDS,
|
|
9883
|
+
...spec.isAsync ? { async: true } : {}
|
|
9884
|
+
};
|
|
9885
|
+
if (!groups.some(hasOurs)) return [...groups, {
|
|
9886
|
+
...spec.matcher === void 0 ? {} : { matcher: spec.matcher },
|
|
9887
|
+
hooks: [handler]
|
|
9888
|
+
}];
|
|
9738
9889
|
return groups.map((group) => hasOurs(group) ? {
|
|
9739
9890
|
...group,
|
|
9740
9891
|
hooks: group.hooks.map((entry) => isOurs(entry) ? handler : entry)
|
|
@@ -9754,8 +9905,29 @@ async function writeCodexConfig(path, text) {
|
|
|
9754
9905
|
await rm(temporary, { force: true });
|
|
9755
9906
|
}
|
|
9756
9907
|
}
|
|
9757
|
-
|
|
9758
|
-
|
|
9908
|
+
// src/init/credentials-api.ts
|
|
9909
|
+
const VISIBILITIES = /* @__PURE__ */ new Set([
|
|
9910
|
+
"all_members",
|
|
9911
|
+
"owners_admins_and_sender",
|
|
9912
|
+
"sender_only"
|
|
9913
|
+
]);
|
|
9914
|
+
const RETENTION_DAYS = /* @__PURE__ */ new Set([
|
|
9915
|
+
7,
|
|
9916
|
+
14,
|
|
9917
|
+
30
|
|
9918
|
+
]);
|
|
9919
|
+
function parseContent(value) {
|
|
9920
|
+
if (!isObject(value)) return void 0;
|
|
9921
|
+
const { prompts, model_and_tools: modelAndTools, visibility } = value;
|
|
9922
|
+
const retentionDays = value.retention_days;
|
|
9923
|
+
if (typeof prompts !== "boolean" || typeof modelAndTools !== "boolean" || typeof visibility !== "string" || !VISIBILITIES.has(visibility) || typeof retentionDays !== "number" || !RETENTION_DAYS.has(retentionDays)) return void 0;
|
|
9924
|
+
return {
|
|
9925
|
+
isPromptsStored: prompts,
|
|
9926
|
+
isModelAndToolsStored: modelAndTools,
|
|
9927
|
+
visibility,
|
|
9928
|
+
retentionDays
|
|
9929
|
+
};
|
|
9930
|
+
}
|
|
9759
9931
|
const WORKSPACE_ID = /^[A-Za-z0-9-]{1,128}$/;
|
|
9760
9932
|
const NO_WHITESPACE = /^\S+$/;
|
|
9761
9933
|
const LIMIT_ROLES = /* @__PURE__ */ new Set([
|
|
@@ -9764,15 +9936,16 @@ const LIMIT_ROLES = /* @__PURE__ */ new Set([
|
|
|
9764
9936
|
"member"
|
|
9765
9937
|
]);
|
|
9766
9938
|
function parseIssued(body) {
|
|
9767
|
-
const { workspace_id: workspaceId, ingest_key: ingestKey, ingest_endpoint: ingestEndpoint,
|
|
9768
|
-
if (typeof workspaceId !== "string" || typeof ingestKey !== "string" || typeof ingestEndpoint !== "string" ||
|
|
9939
|
+
const { workspace_id: workspaceId, ingest_key: ingestKey, ingest_endpoint: ingestEndpoint, content } = body;
|
|
9940
|
+
if (typeof workspaceId !== "string" || typeof ingestKey !== "string" || typeof ingestEndpoint !== "string" || !WORKSPACE_ID.test(workspaceId)) return void 0;
|
|
9769
9941
|
const apiToken = parseTokenPair(body, workspaceId);
|
|
9770
|
-
|
|
9942
|
+
const parsedContent = parseContent(content);
|
|
9943
|
+
return ingestKey.startsWith(`hf_ik_${workspaceId}_`) && NO_WHITESPACE.test(ingestKey) && apiToken !== void 0 && parsedContent !== void 0 && parseAllowedUrl(ingestEndpoint) !== void 0 && apiToken && parsedContent ? {
|
|
9771
9944
|
workspaceId,
|
|
9772
9945
|
ingestKey,
|
|
9773
9946
|
apiToken,
|
|
9774
9947
|
ingestEndpoint,
|
|
9775
|
-
|
|
9948
|
+
content: parsedContent
|
|
9776
9949
|
} : void 0;
|
|
9777
9950
|
}
|
|
9778
9951
|
function classify(status, body) {
|
|
@@ -9812,8 +9985,7 @@ async function requestCredentials(base, request, fetchImpl) {
|
|
|
9812
9985
|
return { kind: "unexpected" };
|
|
9813
9986
|
}
|
|
9814
9987
|
}
|
|
9815
|
-
|
|
9816
|
-
//#region src/init/pkce.ts
|
|
9988
|
+
// src/init/pkce.ts
|
|
9817
9989
|
const RANDOM_BYTES = 32;
|
|
9818
9990
|
const codeChallenge = (codeVerifier) => createHash("sha256").update(codeVerifier).digest("base64url");
|
|
9819
9991
|
function createLoginSecrets() {
|
|
@@ -9824,8 +9996,7 @@ function createLoginSecrets() {
|
|
|
9824
9996
|
state: randomBytes(RANDOM_BYTES).toString("base64url")
|
|
9825
9997
|
};
|
|
9826
9998
|
}
|
|
9827
|
-
|
|
9828
|
-
//#region src/init/init.ts
|
|
9999
|
+
// src/init/init.ts
|
|
9829
10000
|
const CLIENT_ID = "harnessforce-cli";
|
|
9830
10001
|
const DEFAULT_PORT = 8080;
|
|
9831
10002
|
const MAX_REVOKE_HASHES = 100;
|
|
@@ -9856,28 +10027,43 @@ async function runInit(args, deps) {
|
|
|
9856
10027
|
if (settings.kind === "invalid" || !hasWritableMarketplaces(settings.settings)) stop$1("settingsUnreadable");
|
|
9857
10028
|
const authorizationEndpoint = await discoverAuthorizationEndpoint(base, deps.fetch) ?? stop$1("network");
|
|
9858
10029
|
const secrets = createLoginSecrets();
|
|
9859
|
-
const { code, redirectUri } = await logIn(authorizationEndpoint, secrets, port, deps);
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
codexPath,
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
10030
|
+
const { code, redirectUri, loopback } = await logIn(authorizationEndpoint, secrets, port, new URL(recordedUrl).origin, deps);
|
|
10031
|
+
let completion = "failed";
|
|
10032
|
+
try {
|
|
10033
|
+
const outcome = await requestCredentials(base, {
|
|
10034
|
+
code,
|
|
10035
|
+
code_verifier: secrets.codeVerifier,
|
|
10036
|
+
client_id: CLIENT_ID,
|
|
10037
|
+
redirect_uri: redirectUri,
|
|
10038
|
+
revoke_key_hashes: revoke.keyHashes,
|
|
10039
|
+
revoke_api_token_hashes: revoke.apiTokenHashes
|
|
10040
|
+
}, deps.fetch);
|
|
10041
|
+
if (outcome.kind === "viewer") completion = "viewer";
|
|
10042
|
+
const issued = issuedOrStop(outcome);
|
|
10043
|
+
const isPluginAutoUpdateOn = await save(issued, recordedUrl, settingsPath, args, store).catch((error) => {
|
|
10044
|
+
if (isStop(error)) throw error;
|
|
10045
|
+
return stop$1("saveFailed");
|
|
10046
|
+
});
|
|
10047
|
+
const codexPath = codexConfigPath(deps.env, deps.homeDir);
|
|
10048
|
+
await saveCodexConfig(issued, recordedUrl, codexPath, args.sendContent).catch(() => stop$1("codexSaveFailed"));
|
|
10049
|
+
completion = "connected";
|
|
10050
|
+
loopback.finish({
|
|
10051
|
+
status: completion,
|
|
10052
|
+
workspaceId: issued.workspaceId
|
|
10053
|
+
});
|
|
10054
|
+
return finishInit(issued, args, deps, {
|
|
10055
|
+
codexPath,
|
|
10056
|
+
connection: recordedUrl,
|
|
10057
|
+
isKernelKeyring: store.isKernelKeyring,
|
|
10058
|
+
isPluginAutoUpdateOn
|
|
10059
|
+
});
|
|
10060
|
+
} finally {
|
|
10061
|
+
loopback.finish({ status: completion });
|
|
10062
|
+
}
|
|
10063
|
+
}
|
|
10064
|
+
function finishInit(issued, args, deps, result) {
|
|
10065
|
+
deps.stdout(`${contentNotice(issued.content, args.sendContent)}\n`);
|
|
10066
|
+
return result;
|
|
9881
10067
|
}
|
|
9882
10068
|
async function openKeychain(deps, previousWorkspaceId) {
|
|
9883
10069
|
return await deps.keychain.openForInit({
|
|
@@ -9899,11 +10085,12 @@ function revokeHashes({ items }) {
|
|
|
9899
10085
|
apiTokenHashes
|
|
9900
10086
|
};
|
|
9901
10087
|
}
|
|
9902
|
-
async function logIn(authorizationEndpoint, secrets, port, deps) {
|
|
10088
|
+
async function logIn(authorizationEndpoint, secrets, port, webOrigin, deps) {
|
|
9903
10089
|
const loopback = await deps.startLoopback({
|
|
9904
10090
|
state: secrets.state,
|
|
9905
10091
|
timeoutMs: deps.callbackTimeoutMs,
|
|
9906
|
-
port
|
|
10092
|
+
port,
|
|
10093
|
+
webOrigin
|
|
9907
10094
|
}).catch(() => stopWith(listenFailedMessage(port)));
|
|
9908
10095
|
const authorization = new URL(authorizationEndpoint);
|
|
9909
10096
|
for (const [name, value] of Object.entries({
|
|
@@ -9915,10 +10102,23 @@ async function logIn(authorizationEndpoint, secrets, port, deps) {
|
|
|
9915
10102
|
state: secrets.state
|
|
9916
10103
|
})) authorization.searchParams.set(name, value);
|
|
9917
10104
|
if (!await deps.openBrowser(authorization.href)) deps.stderr(`${authorization.href}\n`);
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
10105
|
+
const callback = await loopback.callback ?? stop$1("timeout");
|
|
10106
|
+
try {
|
|
10107
|
+
return {
|
|
10108
|
+
code: codeOrStop(callback, secrets.state),
|
|
10109
|
+
redirectUri: loopback.redirectUri,
|
|
10110
|
+
loopback
|
|
10111
|
+
};
|
|
10112
|
+
} catch (error) {
|
|
10113
|
+
loopback.finish({ status: callbackFailure(callback, secrets.state) });
|
|
10114
|
+
throw error;
|
|
10115
|
+
}
|
|
10116
|
+
}
|
|
10117
|
+
function callbackFailure(callback, state) {
|
|
10118
|
+
if (callback.state !== state) return "failed";
|
|
10119
|
+
if (callback.error === "access_denied") return "denied";
|
|
10120
|
+
if (callback.error === "no_workspace") return "no_workspace";
|
|
10121
|
+
return "failed";
|
|
9922
10122
|
}
|
|
9923
10123
|
function codeOrStop(callback, state) {
|
|
9924
10124
|
if (callback.state !== state) return stop$1("loginFailed");
|
|
@@ -9961,21 +10161,35 @@ async function save(issued, connection, settingsPath, args, store) {
|
|
|
9961
10161
|
OTEL_TRACES_EXPORTER: "otlp",
|
|
9962
10162
|
OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf"
|
|
9963
10163
|
};
|
|
9964
|
-
|
|
10164
|
+
Object.assign(env, contentEnv(issued.content, args.sendContent));
|
|
9965
10165
|
const { settings, isOn } = withPluginAutoUpdate(mergeUserSettings(current.settings, env), args.noPluginAutoUpdate);
|
|
9966
10166
|
await writeUserSettings(settingsPath, settings);
|
|
9967
10167
|
return isOn;
|
|
9968
10168
|
}
|
|
10169
|
+
function contentEnv(content, sendContent) {
|
|
10170
|
+
const flag = (isStored) => sendContent && isStored ? "1" : "0";
|
|
10171
|
+
const modelAndTools = flag(content.isModelAndToolsStored);
|
|
10172
|
+
return {
|
|
10173
|
+
OTEL_LOG_USER_PROMPTS: flag(content.isPromptsStored),
|
|
10174
|
+
OTEL_LOG_ASSISTANT_RESPONSES: modelAndTools,
|
|
10175
|
+
OTEL_LOG_TOOL_DETAILS: modelAndTools,
|
|
10176
|
+
OTEL_LOG_TOOL_CONTENT: modelAndTools
|
|
10177
|
+
};
|
|
10178
|
+
}
|
|
10179
|
+
function contentNotice(content, sendContent) {
|
|
10180
|
+
if (!sendContent) return INIT_MESSAGES.contentNotSent;
|
|
10181
|
+
return content.isPromptsStored || content.isModelAndToolsStored ? contentEnabledMessage(content) : INIT_MESSAGES.contentNotStored;
|
|
10182
|
+
}
|
|
9969
10183
|
async function saveCodexConfig(issued, connection, path, sendContent) {
|
|
9970
10184
|
await writeCodexConfig(path, mergeCodexConfig(await readCodexConfig(path), {
|
|
9971
10185
|
connection,
|
|
9972
10186
|
ingestEndpoint: issued.ingestEndpoint,
|
|
9973
10187
|
ingestKey: issued.ingestKey,
|
|
9974
|
-
logUserPrompt: sendContent && issued.
|
|
10188
|
+
logUserPrompt: sendContent && issued.content.isPromptsStored,
|
|
10189
|
+
logAgentResponses: sendContent && issued.content.isModelAndToolsStored
|
|
9975
10190
|
}));
|
|
9976
10191
|
}
|
|
9977
|
-
|
|
9978
|
-
//#region src/otel-headers.ts
|
|
10192
|
+
// src/otel-headers.ts
|
|
9979
10193
|
const DESTINATION_VARIABLES = [
|
|
9980
10194
|
"HARNESSFORCE_ENDPOINT",
|
|
9981
10195
|
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
@@ -10029,8 +10243,7 @@ async function selectKey(env, managedDir, unlimited) {
|
|
|
10029
10243
|
function isPinnedDestination(env, pinnedOrigin) {
|
|
10030
10244
|
return DESTINATION_VARIABLES.map((name) => env[name]).filter((value) => Boolean(value)).every((value) => parseAllowedUrl(value)?.origin === pinnedOrigin);
|
|
10031
10245
|
}
|
|
10032
|
-
|
|
10033
|
-
//#region ../agent-core/src/config/plugins.ts
|
|
10246
|
+
// ../agent-core/src/config/plugins.ts
|
|
10034
10247
|
const NON_MARKETPLACE_ORIGINS = /* @__PURE__ */ new Set([
|
|
10035
10248
|
"inline",
|
|
10036
10249
|
"skills-dir",
|
|
@@ -10071,8 +10284,7 @@ async function collectPlugins(pluginsRoot, settingsByPrecedence, scope, guard) {
|
|
|
10071
10284
|
collectMcpServers$1(s, (await readJsonObjectFile(join(root, ".mcp.json"), guard))?.mcpServers);
|
|
10072
10285
|
}
|
|
10073
10286
|
}
|
|
10074
|
-
|
|
10075
|
-
//#region ../agent-core/src/config/collect.ts
|
|
10287
|
+
// ../agent-core/src/config/collect.ts
|
|
10076
10288
|
const COLLECT_BUDGET_MS = 1e3;
|
|
10077
10289
|
async function readManagedSettings(managedDir, guard) {
|
|
10078
10290
|
let merged = await readJsonObjectFile(join(managedDir, "managed-settings.json"), guard);
|
|
@@ -10206,8 +10418,7 @@ async function collectConfig(options) {
|
|
|
10206
10418
|
components: sortComponents(sink.components)
|
|
10207
10419
|
};
|
|
10208
10420
|
}
|
|
10209
|
-
|
|
10210
|
-
//#region src/run/context.ts
|
|
10421
|
+
// src/run/context.ts
|
|
10211
10422
|
async function resolveConfigVersion(options) {
|
|
10212
10423
|
const startedMs = options.now().getTime();
|
|
10213
10424
|
const result = await collectConfig({
|
|
@@ -10227,8 +10438,7 @@ async function resolveLaunchContext(options) {
|
|
|
10227
10438
|
...configVersion === void 0 ? {} : { configVersion }
|
|
10228
10439
|
};
|
|
10229
10440
|
}
|
|
10230
|
-
|
|
10231
|
-
//#region src/run/issues.ts
|
|
10441
|
+
// src/run/issues.ts
|
|
10232
10442
|
const MAX_CANDIDATES = 10;
|
|
10233
10443
|
const DOT_SEGMENT = /^\.{1,2}$/;
|
|
10234
10444
|
async function get(url, apiToken, fetchImpl, timeoutMs) {
|
|
@@ -10287,8 +10497,7 @@ async function resolveIssue(base, identifier, apiToken, fetchImpl, timeoutMs = R
|
|
|
10287
10497
|
}
|
|
10288
10498
|
return fetchCandidates(base, identifier, apiToken, fetchImpl, timeoutMs);
|
|
10289
10499
|
}
|
|
10290
|
-
|
|
10291
|
-
//#region src/run/launch.ts
|
|
10500
|
+
// src/run/launch.ts
|
|
10292
10501
|
const STRIPPED_FROM_SHELL = /* @__PURE__ */ new Set(["OTEL_EXPORTER_OTLP_HEADERS", ...[
|
|
10293
10502
|
"TRACES",
|
|
10294
10503
|
"LOGS",
|
|
@@ -10350,8 +10559,7 @@ function buildLaunch(request) {
|
|
|
10350
10559
|
...isClaudeCode(request.agent) ? { settingsEnv: injectedEnv } : {}
|
|
10351
10560
|
};
|
|
10352
10561
|
}
|
|
10353
|
-
|
|
10354
|
-
//#region src/run/messages.ts
|
|
10562
|
+
// src/run/messages.ts
|
|
10355
10563
|
const RUN_MESSAGES = {
|
|
10356
10564
|
keychainUnavailable: INIT_MESSAGES.keychainUnavailable,
|
|
10357
10565
|
initRequired: "Run `harnessforce init`",
|
|
@@ -10364,8 +10572,7 @@ const RUN_MESSAGES = {
|
|
|
10364
10572
|
candidatesHeader: "Could not resolve the Issue. Candidates:"
|
|
10365
10573
|
};
|
|
10366
10574
|
const launchFailedMessage = (agent) => `Could not start ${agent}`;
|
|
10367
|
-
|
|
10368
|
-
//#region src/run/run.ts
|
|
10575
|
+
// src/run/run.ts
|
|
10369
10576
|
const stop = (message) => stopWith(RUN_MESSAGES[message]);
|
|
10370
10577
|
const printable = (text) => text.replace(/\p{Cc}/gu, "");
|
|
10371
10578
|
function candidateList(candidates) {
|
|
@@ -10444,8 +10651,7 @@ async function runWithoutHarnessforce(args, deps) {
|
|
|
10444
10651
|
deps.stderr(`${launchFailedMessage(args.agent)}\n`);
|
|
10445
10652
|
return 1;
|
|
10446
10653
|
}
|
|
10447
|
-
|
|
10448
|
-
//#region src/top/args.ts
|
|
10654
|
+
// src/top/args.ts
|
|
10449
10655
|
const TOP_USAGE = "Usage: harnessforce top [--once] [--json] [--ascii] [--theme <auto|light|dark|ansi>]\n";
|
|
10450
10656
|
const THEMES = [
|
|
10451
10657
|
"auto",
|
|
@@ -10474,8 +10680,7 @@ function parseTopArgs(args) {
|
|
|
10474
10680
|
theme
|
|
10475
10681
|
};
|
|
10476
10682
|
}
|
|
10477
|
-
|
|
10478
|
-
//#region src/top/width.ts
|
|
10683
|
+
// src/top/width.ts
|
|
10479
10684
|
const SGR = /\x1b\[[0-9;]*m/g;
|
|
10480
10685
|
const stripSgr = (text) => text.replace(SGR, "");
|
|
10481
10686
|
const WIDE_RANGES = [
|
|
@@ -10540,8 +10745,7 @@ function truncate(text, maxWidth, ambiguousWide, ellipsis) {
|
|
|
10540
10745
|
function padEnd(text, width, ambiguousWide) {
|
|
10541
10746
|
return text + " ".repeat(Math.max(0, width - displayWidth(text, ambiguousWide)));
|
|
10542
10747
|
}
|
|
10543
|
-
|
|
10544
|
-
//#region src/top/frame.ts
|
|
10748
|
+
// src/top/frame.ts
|
|
10545
10749
|
const plainPainter = {
|
|
10546
10750
|
color: (_token, text) => text,
|
|
10547
10751
|
bold: (text) => text,
|
|
@@ -10741,8 +10945,7 @@ function renderDetail(row, options) {
|
|
|
10741
10945
|
];
|
|
10742
10946
|
}
|
|
10743
10947
|
const skippedNotice = (snapshot) => snapshot.skippedLines > 0 || snapshot.skippedFiles > 0 ? [`Skipped ${snapshot.skippedLines} unreadable line(s) and ${snapshot.skippedFiles} unreadable file(s)`] : [];
|
|
10744
|
-
|
|
10745
|
-
//#region src/top/codex-session.ts
|
|
10948
|
+
// src/top/codex-session.ts
|
|
10746
10949
|
const DETACHED = "HEAD";
|
|
10747
10950
|
const SHELL = "shell";
|
|
10748
10951
|
const COUNTED_CALLS = /* @__PURE__ */ new Set(["function_call", "custom_tool_call"]);
|
|
@@ -10860,8 +11063,7 @@ var CodexSessionAccumulator = class {
|
|
|
10860
11063
|
};
|
|
10861
11064
|
}
|
|
10862
11065
|
};
|
|
10863
|
-
|
|
10864
|
-
//#region src/top/reader.ts
|
|
11066
|
+
// src/top/reader.ts
|
|
10865
11067
|
const WINDOW_MS = 864e5;
|
|
10866
11068
|
const freshState = (newAccumulator) => ({
|
|
10867
11069
|
offset: 0,
|
|
@@ -10986,8 +11188,7 @@ function createTopReader({ projectsDir, codexSessionsDir, git }) {
|
|
|
10986
11188
|
};
|
|
10987
11189
|
} };
|
|
10988
11190
|
}
|
|
10989
|
-
|
|
10990
|
-
//#region src/top/color.ts
|
|
11191
|
+
// src/top/color.ts
|
|
10991
11192
|
const MIN_CONTRAST = 3;
|
|
10992
11193
|
const TOKEN_VALUES = {
|
|
10993
11194
|
good: [[
|
|
@@ -11126,8 +11327,7 @@ function createPainter(input) {
|
|
|
11126
11327
|
dim: (text) => open$1("2", text, "22")
|
|
11127
11328
|
};
|
|
11128
11329
|
}
|
|
11129
|
-
|
|
11130
|
-
//#region src/top/screen.ts
|
|
11330
|
+
// src/top/screen.ts
|
|
11131
11331
|
const ENTER = "\x1B[?1049h\x1B[?25l";
|
|
11132
11332
|
const RESTORE = "\x1B[?25h\x1B[?1049l";
|
|
11133
11333
|
const HOME = "\x1B[H";
|
|
@@ -11237,7 +11437,7 @@ async function runScreen(options) {
|
|
|
11237
11437
|
};
|
|
11238
11438
|
const indexOfSelected = (rows) => {
|
|
11239
11439
|
const index = rows.findIndex((row) => row.sessionId === selectedId);
|
|
11240
|
-
return index
|
|
11440
|
+
return Math.max(index, 0);
|
|
11241
11441
|
};
|
|
11242
11442
|
const select = (rows, index) => {
|
|
11243
11443
|
selectedId = rows[Math.max(0, Math.min(index, rows.length - 1))]?.sessionId;
|
|
@@ -11347,8 +11547,7 @@ async function runScreen(options) {
|
|
|
11347
11547
|
render();
|
|
11348
11548
|
});
|
|
11349
11549
|
}
|
|
11350
|
-
|
|
11351
|
-
//#region src/top/settings.ts
|
|
11550
|
+
// src/top/settings.ts
|
|
11352
11551
|
const DEFAULT_SETTINGS = {
|
|
11353
11552
|
ambiguousWide: false,
|
|
11354
11553
|
braille: false
|
|
@@ -11383,8 +11582,7 @@ async function readTopSettings(homeDir) {
|
|
|
11383
11582
|
braille: braille === true
|
|
11384
11583
|
} };
|
|
11385
11584
|
}
|
|
11386
|
-
|
|
11387
|
-
//#region src/top/command.ts
|
|
11585
|
+
// src/top/command.ts
|
|
11388
11586
|
const iso$1 = (ms) => new Date(ms).toISOString();
|
|
11389
11587
|
function toJson(row, nowMs) {
|
|
11390
11588
|
return {
|
|
@@ -11447,8 +11645,7 @@ async function topCommand(args, deps) {
|
|
|
11447
11645
|
deps.stdout(`${lines.join("\n")}\n`);
|
|
11448
11646
|
return 0;
|
|
11449
11647
|
}
|
|
11450
|
-
|
|
11451
|
-
//#region src/tune/mcp-config.ts
|
|
11648
|
+
// src/tune/mcp-config.ts
|
|
11452
11649
|
const UNLISTED = "unlisted";
|
|
11453
11650
|
const PLUGIN_SERVER = /^([A-Za-z0-9._-]+)@[A-Za-z0-9._-]+:(.+)$/;
|
|
11454
11651
|
const toToolName = (name) => name.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
@@ -11468,8 +11665,7 @@ function mcpConfigOf(identifiers) {
|
|
|
11468
11665
|
}
|
|
11469
11666
|
};
|
|
11470
11667
|
}
|
|
11471
|
-
|
|
11472
|
-
//#region src/tune/vocabulary.ts
|
|
11668
|
+
// src/tune/vocabulary.ts
|
|
11473
11669
|
const CONTINUE_PHRASES = /* @__PURE__ */ new Set([
|
|
11474
11670
|
"continue",
|
|
11475
11671
|
"go",
|
|
@@ -11530,8 +11726,7 @@ const EDIT_TOOLS = /* @__PURE__ */ new Set([
|
|
|
11530
11726
|
"NotebookEdit"
|
|
11531
11727
|
]);
|
|
11532
11728
|
const ISSUE_TOOL = /^mcp__.+__get_issue$/;
|
|
11533
|
-
|
|
11534
|
-
//#region src/tune/analyze.ts
|
|
11729
|
+
// src/tune/analyze.ts
|
|
11535
11730
|
const ANALYZER_VERSION = "1.1.0";
|
|
11536
11731
|
const NOT_MEASURED_INTERVENTIONS = /* @__PURE__ */ new Set(["approval", "answer"]);
|
|
11537
11732
|
const emptyProposalCounts = () => Object.fromEntries(PROPOSAL_KINDS.map((kind) => [kind, {
|
|
@@ -11720,8 +11915,7 @@ function analyzeSession(input) {
|
|
|
11720
11915
|
records_skipped: input.skippedLines
|
|
11721
11916
|
};
|
|
11722
11917
|
}
|
|
11723
|
-
|
|
11724
|
-
//#region src/tune/config-snapshot.ts
|
|
11918
|
+
// src/tune/config-snapshot.ts
|
|
11725
11919
|
async function collectAt(projectRoot, deps, startedMs = deps.now()) {
|
|
11726
11920
|
const result = await collectConfig({
|
|
11727
11921
|
projectRoot,
|
|
@@ -11740,8 +11934,7 @@ async function collectMcpServers(cwd, git, deps) {
|
|
|
11740
11934
|
if (collected.kind === "fixed_failure") return [];
|
|
11741
11935
|
return [...new Set(collected.components.filter((c) => c.kind === "mcp_server").map((c) => c.id))];
|
|
11742
11936
|
}
|
|
11743
|
-
|
|
11744
|
-
//#region src/tune/applied.ts
|
|
11937
|
+
// src/tune/applied.ts
|
|
11745
11938
|
async function detectApplied(proposals, snapshot) {
|
|
11746
11939
|
const byRoot = /* @__PURE__ */ new Map();
|
|
11747
11940
|
const collect = (root) => {
|
|
@@ -11765,8 +11958,7 @@ async function detectApplied(proposals, snapshot) {
|
|
|
11765
11958
|
}
|
|
11766
11959
|
return detected;
|
|
11767
11960
|
}
|
|
11768
|
-
|
|
11769
|
-
//#region src/tune/usage.ts
|
|
11961
|
+
// src/tune/usage.ts
|
|
11770
11962
|
const USAGE_KINDS = [
|
|
11771
11963
|
"frequent_compaction",
|
|
11772
11964
|
"low_cache_reuse",
|
|
@@ -11837,8 +12029,7 @@ function modelShares(usages) {
|
|
|
11837
12029
|
subagent: shares(subagent)
|
|
11838
12030
|
};
|
|
11839
12031
|
}
|
|
11840
|
-
|
|
11841
|
-
//#region src/tune/display.ts
|
|
12032
|
+
// src/tune/display.ts
|
|
11842
12033
|
const median = (values) => {
|
|
11843
12034
|
if (values.length === 0) return null;
|
|
11844
12035
|
const sorted = [...values].sort((a, b) => a - b);
|
|
@@ -11906,8 +12097,7 @@ function localIso(ms) {
|
|
|
11906
12097
|
const pad = (n) => String(n).padStart(2, "0");
|
|
11907
12098
|
return `${new Date(ms + offsetMinutes * 6e4).toISOString().slice(0, 19)}${sign}${pad(Math.floor(absolute / 60))}:${pad(absolute % 60)}`;
|
|
11908
12099
|
}
|
|
11909
|
-
|
|
11910
|
-
//#region src/tune/followups.ts
|
|
12100
|
+
// src/tune/followups.ts
|
|
11911
12101
|
const PERIOD_MS = 12096e5;
|
|
11912
12102
|
const MIN_SESSIONS = 5;
|
|
11913
12103
|
const ratio = (numerator, denominator) => denominator === 0 ? null : numerator / denominator;
|
|
@@ -12011,8 +12201,7 @@ function computeFollowups(proposals, sessions, nowMs, rangeSinceMs) {
|
|
|
12011
12201
|
ended
|
|
12012
12202
|
};
|
|
12013
12203
|
}
|
|
12014
|
-
|
|
12015
|
-
//#region src/tune/lock.ts
|
|
12204
|
+
// src/tune/lock.ts
|
|
12016
12205
|
const LOCK_WAIT_MS = 3e4;
|
|
12017
12206
|
const STALE_LOCK_MS = 6e4;
|
|
12018
12207
|
const HEARTBEAT_MS = 1e4;
|
|
@@ -12047,8 +12236,7 @@ async function acquireTuneLock(path, deps) {
|
|
|
12047
12236
|
await deps.sleep(RETRY_INTERVAL_MS);
|
|
12048
12237
|
}
|
|
12049
12238
|
}
|
|
12050
|
-
|
|
12051
|
-
//#region src/tune/messages.ts
|
|
12239
|
+
// src/tune/messages.ts
|
|
12052
12240
|
const NOTICE_ORDER = [
|
|
12053
12241
|
"insufficient",
|
|
12054
12242
|
"outOfRange",
|
|
@@ -12122,8 +12310,7 @@ var Notices = class {
|
|
|
12122
12310
|
return code;
|
|
12123
12311
|
}
|
|
12124
12312
|
};
|
|
12125
|
-
|
|
12126
|
-
//#region src/tune/store.ts
|
|
12313
|
+
// src/tune/store.ts
|
|
12127
12314
|
const tuneDir = (homeDir) => join(homeDir, ".harnessforce", "tune");
|
|
12128
12315
|
const tunePaths = (homeDir) => {
|
|
12129
12316
|
const dir = tuneDir(homeDir);
|
|
@@ -12169,8 +12356,7 @@ async function writeFileAtomic(path, content) {
|
|
|
12169
12356
|
}
|
|
12170
12357
|
}
|
|
12171
12358
|
const writeJsonFile = (path, value) => writeFileAtomic(path, `${JSON.stringify(value)}\n`);
|
|
12172
|
-
|
|
12173
|
-
//#region src/tune/proposals.ts
|
|
12359
|
+
// src/tune/proposals.ts
|
|
12174
12360
|
const isRecord = (value) => isObject(value) && typeof value.proposal_id === "string" && typeof value.change_type === "string" && PROPOSAL_KINDS.includes(value.change_type) && Array.isArray(value.evidence_session_ids) && (value.attribution === "pending" || value.attribution === "decided");
|
|
12175
12361
|
async function readProposals(path) {
|
|
12176
12362
|
const value = await readJsonFile(path);
|
|
@@ -12209,8 +12395,7 @@ function countProposals(proposals, sessionId) {
|
|
|
12209
12395
|
}
|
|
12210
12396
|
return counts;
|
|
12211
12397
|
}
|
|
12212
|
-
|
|
12213
|
-
//#region src/tune/saved-state.ts
|
|
12398
|
+
// src/tune/saved-state.ts
|
|
12214
12399
|
async function readDestinations(path) {
|
|
12215
12400
|
const value = await readJsonFile(path);
|
|
12216
12401
|
return isObject(value) && isObject(value.destinations) ? value.destinations : {};
|
|
@@ -12269,8 +12454,7 @@ const writeSessionConfigs = (path, configs) => writeJsonFile(path, {
|
|
|
12269
12454
|
transcript_path: config.transcriptPath
|
|
12270
12455
|
}]))
|
|
12271
12456
|
});
|
|
12272
|
-
|
|
12273
|
-
//#region src/tune/send.ts
|
|
12457
|
+
// src/tune/send.ts
|
|
12274
12458
|
const BATCH_SIZE = 100;
|
|
12275
12459
|
const READ_ONLY = "workspace_read_only";
|
|
12276
12460
|
function parseReply(body, size) {
|
|
@@ -12371,8 +12555,7 @@ async function sendReports(options) {
|
|
|
12371
12555
|
result.invalid.reasons = [...reasons];
|
|
12372
12556
|
return result;
|
|
12373
12557
|
}
|
|
12374
|
-
|
|
12375
|
-
//#region src/tune/sessions.ts
|
|
12558
|
+
// src/tune/sessions.ts
|
|
12376
12559
|
const byCodeUnit = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
12377
12560
|
async function readSubagentUsages(transcriptPath) {
|
|
12378
12561
|
const dir = join(dirname(transcriptPath), basename(transcriptPath, ".jsonl"), "subagents");
|
|
@@ -12421,8 +12604,7 @@ async function readTuneSessions(options) {
|
|
|
12421
12604
|
}
|
|
12422
12605
|
return [...sessions.values()].sort((a, b) => Date.parse(a.session.startedAt) - Date.parse(b.session.startedAt) || byCodeUnit(a.session.sessionId, b.session.sessionId));
|
|
12423
12606
|
}
|
|
12424
|
-
|
|
12425
|
-
//#region src/tune/settings.ts
|
|
12607
|
+
// src/tune/settings.ts
|
|
12426
12608
|
const tuneConfigPath = (homeDir) => join(homeDir, ".harnessforce", "config.json");
|
|
12427
12609
|
const isMissing = (error) => error?.code === "ENOENT";
|
|
12428
12610
|
async function readSendSetting(homeDir) {
|
|
@@ -12447,8 +12629,7 @@ async function readSendSetting(homeDir) {
|
|
|
12447
12629
|
if (typeof sendReport !== "boolean") return "unreadable";
|
|
12448
12630
|
return sendReport ? "send" : "no_send";
|
|
12449
12631
|
}
|
|
12450
|
-
|
|
12451
|
-
//#region src/tune/command.ts
|
|
12632
|
+
// src/tune/command.ts
|
|
12452
12633
|
const DAY_MS = 864e5;
|
|
12453
12634
|
const DEFAULT_RANGE_DAYS = 30;
|
|
12454
12635
|
const SKIPPED_RATIO = .05;
|
|
@@ -12730,8 +12911,7 @@ function analyzeCommand(options, deps) {
|
|
|
12730
12911
|
}
|
|
12731
12912
|
}, deps.stderr);
|
|
12732
12913
|
}
|
|
12733
|
-
|
|
12734
|
-
//#region src/tune/purge.ts
|
|
12914
|
+
// src/tune/purge.ts
|
|
12735
12915
|
async function purgeTuneFiles(paths) {
|
|
12736
12916
|
const targets = [
|
|
12737
12917
|
paths.repositories,
|
|
@@ -12746,8 +12926,7 @@ async function purgeTuneFiles(paths) {
|
|
|
12746
12926
|
force: true
|
|
12747
12927
|
})))).every((r) => r.status === "fulfilled");
|
|
12748
12928
|
}
|
|
12749
|
-
|
|
12750
|
-
//#region src/tune/analysis-file.ts
|
|
12929
|
+
// src/tune/analysis-file.ts
|
|
12751
12930
|
async function readAnalysisSessions(path) {
|
|
12752
12931
|
const value = await readJsonFile(path);
|
|
12753
12932
|
const sessions = isObject(value) && Array.isArray(value.sessions) ? value.sessions : [];
|
|
@@ -12762,8 +12941,7 @@ async function readAnalysisSessions(path) {
|
|
|
12762
12941
|
}
|
|
12763
12942
|
return found;
|
|
12764
12943
|
}
|
|
12765
|
-
|
|
12766
|
-
//#region src/tune/record.ts
|
|
12944
|
+
// src/tune/record.ts
|
|
12767
12945
|
const MAX_RECORD_INPUT_BYTES = 1048576;
|
|
12768
12946
|
const SCOPES = [
|
|
12769
12947
|
"user",
|
|
@@ -12927,8 +13105,7 @@ async function recordProposal(text, paths, now) {
|
|
|
12927
13105
|
attributed_session_id: record.attributed_session_id
|
|
12928
13106
|
};
|
|
12929
13107
|
}
|
|
12930
|
-
|
|
12931
|
-
//#region src/tune/tune.ts
|
|
13108
|
+
// src/tune/tune.ts
|
|
12932
13109
|
const FLAGS = {
|
|
12933
13110
|
"--all": "all",
|
|
12934
13111
|
"--no-send": "noSend",
|
|
@@ -13010,8 +13187,7 @@ async function tuneCommand(args, deps) {
|
|
|
13010
13187
|
});
|
|
13011
13188
|
return analyzeCommand(parsed.options, deps);
|
|
13012
13189
|
}
|
|
13013
|
-
|
|
13014
|
-
//#region src/main.ts
|
|
13190
|
+
// src/main.ts
|
|
13015
13191
|
const { version } = createRequire(import.meta.url)("../package.json");
|
|
13016
13192
|
const EXIT_CLIENT_UNSUPPORTED = 5;
|
|
13017
13193
|
function parseInitArgs(args) {
|
|
@@ -13114,11 +13290,8 @@ async function run(argv, deps) {
|
|
|
13114
13290
|
now: () => cli.now().getTime()
|
|
13115
13291
|
}));
|
|
13116
13292
|
if (command === "hook") {
|
|
13117
|
-
|
|
13118
|
-
|
|
13119
|
-
return 1;
|
|
13120
|
-
}
|
|
13121
|
-
return sessionStart({
|
|
13293
|
+
const [event] = rest;
|
|
13294
|
+
const hookDeps = {
|
|
13122
13295
|
env: cli.env,
|
|
13123
13296
|
homeDir: cli.homeDir,
|
|
13124
13297
|
now: cli.now,
|
|
@@ -13130,7 +13303,14 @@ async function run(argv, deps) {
|
|
|
13130
13303
|
stderr: cli.stderr,
|
|
13131
13304
|
client,
|
|
13132
13305
|
defaultUrl: cli.defaultUrl
|
|
13306
|
+
};
|
|
13307
|
+
if (rest.length === 1 && event === "session-start") return sessionStart(hookDeps);
|
|
13308
|
+
if (rest.length === 1 && (event === "post-tool-use" || event === "stop")) return workLocationHook(event, {
|
|
13309
|
+
...hookDeps,
|
|
13310
|
+
hookTmpDir: cli.hookTmpDir
|
|
13133
13311
|
});
|
|
13312
|
+
cli.stderr(HOOK_USAGE);
|
|
13313
|
+
return 1;
|
|
13134
13314
|
}
|
|
13135
13315
|
if (command === "top") return topCommand(rest, {
|
|
13136
13316
|
homeDir: cli.homeDir,
|
|
@@ -13156,8 +13336,7 @@ async function run(argv, deps) {
|
|
|
13156
13336
|
cli.stderr(UNKNOWN_COMMAND_HINT);
|
|
13157
13337
|
return 1;
|
|
13158
13338
|
}
|
|
13159
|
-
|
|
13160
|
-
//#region src/relaunch.ts
|
|
13339
|
+
// src/relaunch.ts
|
|
13161
13340
|
const SENDING_COMMANDS = /* @__PURE__ */ new Set([
|
|
13162
13341
|
"init",
|
|
13163
13342
|
"run",
|
|
@@ -13185,8 +13364,7 @@ async function relaunchHf(argv, deps) {
|
|
|
13185
13364
|
deps.stderr(`${RESTART_FAILED}\n`);
|
|
13186
13365
|
return 1;
|
|
13187
13366
|
}
|
|
13188
|
-
|
|
13189
|
-
//#region src/run/command.ts
|
|
13367
|
+
// src/run/command.ts
|
|
13190
13368
|
const hasSeparator = (command, platform) => platform === "win32" ? /[\\/]/.test(command) : command.includes("/");
|
|
13191
13369
|
const contextFor = ({ platform, env, cwd, fs }) => ({
|
|
13192
13370
|
platform,
|
|
@@ -13202,8 +13380,7 @@ async function resolveAgentFile(command, options) {
|
|
|
13202
13380
|
function commandLine(file, args, options) {
|
|
13203
13381
|
return commandLineFor(file, args, contextFor(options));
|
|
13204
13382
|
}
|
|
13205
|
-
|
|
13206
|
-
//#region src/run/process.ts
|
|
13383
|
+
// src/run/process.ts
|
|
13207
13384
|
const IGNORED_WHILE_RUNNING = ["SIGINT", "SIGQUIT"];
|
|
13208
13385
|
const FORWARDED = ["SIGTERM", "SIGHUP"];
|
|
13209
13386
|
const FAILED = { kind: "failed" };
|
|
@@ -13265,8 +13442,7 @@ function spawnAndWait(command, env) {
|
|
|
13265
13442
|
}));
|
|
13266
13443
|
});
|
|
13267
13444
|
}
|
|
13268
|
-
|
|
13269
|
-
//#region src/top/process-io.ts
|
|
13445
|
+
// src/top/process-io.ts
|
|
13270
13446
|
const EXIT_SIGNALS = ["SIGTERM", "SIGHUP"];
|
|
13271
13447
|
function createProcessIo(proc = process) {
|
|
13272
13448
|
const { stdin, stdout } = proc;
|
|
@@ -13327,8 +13503,7 @@ function createProcessIo(proc = process) {
|
|
|
13327
13503
|
}
|
|
13328
13504
|
};
|
|
13329
13505
|
}
|
|
13330
|
-
|
|
13331
|
-
//#region src/bin.ts
|
|
13506
|
+
// src/bin.ts
|
|
13332
13507
|
const require = createRequire(import.meta.url);
|
|
13333
13508
|
const { url: defaultUrl, isDefault } = require("./build-config.json");
|
|
13334
13509
|
const CALLBACK_TIMEOUT_MS = 3e5;
|
|
@@ -13390,6 +13565,7 @@ process.exitCode = relaunched ?? await run(argv, {
|
|
|
13390
13565
|
processCwd: process.cwd(),
|
|
13391
13566
|
excludeTarget: true
|
|
13392
13567
|
}),
|
|
13568
|
+
hookTmpDir: tmpdir(),
|
|
13393
13569
|
sleep: (ms) => setTimeout$1(ms),
|
|
13394
13570
|
top: createProcessIo(),
|
|
13395
13571
|
readStdin: async (maxBytes) => {
|
|
@@ -13403,5 +13579,4 @@ process.exitCode = relaunched ?? await run(argv, {
|
|
|
13403
13579
|
return Buffer.concat(chunks);
|
|
13404
13580
|
}
|
|
13405
13581
|
});
|
|
13406
|
-
//#endregion
|
|
13407
13582
|
export {};
|