@ascenda-one/history-import 0.1.12 → 0.1.13
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 +63 -41
- package/dist/archive.js +446 -0
- package/dist/archive.js.map +1 -0
- package/dist/cli.js +1398 -309
- package/dist/cli.js.map +1 -1
- package/dist/extractors/claudeCode.js +51 -49
- package/dist/extractors/claudeCode.js.map +1 -1
- package/dist/extractors/cursor.js +18 -21
- package/dist/extractors/cursor.js.map +1 -1
- package/dist/extractors/git.js +1 -1
- package/dist/extractors/git.js.map +1 -1
- package/dist/extractors/vscode.js +54 -35
- package/dist/extractors/vscode.js.map +1 -1
- package/dist/localHandoff.js +13 -3
- package/dist/localHandoff.js.map +1 -1
- package/dist/ship.js +102 -9
- package/dist/ship.js.map +1 -1
- package/dist/staging.js +175 -4
- package/dist/staging.js.map +1 -1
- package/dist/types.js +13 -17
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -242,10 +242,29 @@ var require_afterHours = __commonJS({
|
|
|
242
242
|
"../packages/tool-kit/out/afterHours.js"(exports) {
|
|
243
243
|
"use strict";
|
|
244
244
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
245
|
-
exports.
|
|
245
|
+
exports.BUSINESS_DAY = void 0;
|
|
246
|
+
exports.isOutsideBusinessHours = isOutsideBusinessHours2;
|
|
247
|
+
exports.isAfterHours = isAfterHours3;
|
|
246
248
|
exports.utcOffsetMinutesAt = utcOffsetMinutesAt2;
|
|
247
249
|
exports.localHourAt = localHourAt;
|
|
248
|
-
|
|
250
|
+
exports.BUSINESS_DAY = {
|
|
251
|
+
/** 0 = Sunday … 6 = Saturday. */
|
|
252
|
+
days: [1, 2, 3, 4, 5],
|
|
253
|
+
start: "09:00",
|
|
254
|
+
end: "17:00"
|
|
255
|
+
};
|
|
256
|
+
function isOutsideBusinessHours2(at = /* @__PURE__ */ new Date(), opts = {}) {
|
|
257
|
+
const days = opts.days ?? exports.BUSINESS_DAY.days;
|
|
258
|
+
if (!days.includes(at.getDay()))
|
|
259
|
+
return true;
|
|
260
|
+
const minutes = at.getHours() * 60 + at.getMinutes();
|
|
261
|
+
const start = parseTimeToMinutes(opts.start ?? exports.BUSINESS_DAY.start, 9 * 60);
|
|
262
|
+
const end = parseTimeToMinutes(opts.end ?? exports.BUSINESS_DAY.end, 17 * 60);
|
|
263
|
+
if (start >= end)
|
|
264
|
+
return false;
|
|
265
|
+
return minutes < start || minutes >= end;
|
|
266
|
+
}
|
|
267
|
+
function isAfterHours3(now = /* @__PURE__ */ new Date(), start = "19:00", end = "07:00") {
|
|
249
268
|
const currentMinutes = now.getHours() * 60 + now.getMinutes();
|
|
250
269
|
const startMinutes = parseTimeToMinutes(start, 19 * 60);
|
|
251
270
|
const endMinutes = parseTimeToMinutes(end, 7 * 60);
|
|
@@ -304,9 +323,9 @@ var require_payload = __commonJS({
|
|
|
304
323
|
}
|
|
305
324
|
return void 0;
|
|
306
325
|
}
|
|
307
|
-
function getNested(input,
|
|
326
|
+
function getNested(input, path10) {
|
|
308
327
|
let current = input;
|
|
309
|
-
for (const segment of
|
|
328
|
+
for (const segment of path10) {
|
|
310
329
|
if (!current || typeof current !== "object")
|
|
311
330
|
return void 0;
|
|
312
331
|
current = current[segment];
|
|
@@ -314,16 +333,16 @@ var require_payload = __commonJS({
|
|
|
314
333
|
return current;
|
|
315
334
|
}
|
|
316
335
|
function getNestedString(input, paths) {
|
|
317
|
-
for (const
|
|
318
|
-
const value = getNested(input,
|
|
336
|
+
for (const path10 of paths) {
|
|
337
|
+
const value = getNested(input, path10);
|
|
319
338
|
if (typeof value === "string" && value.trim())
|
|
320
339
|
return value;
|
|
321
340
|
}
|
|
322
341
|
return void 0;
|
|
323
342
|
}
|
|
324
343
|
function getNestedNumber(input, paths) {
|
|
325
|
-
for (const
|
|
326
|
-
const value = getNested(input,
|
|
344
|
+
for (const path10 of paths) {
|
|
345
|
+
const value = getNested(input, path10);
|
|
327
346
|
if (typeof value === "number" && Number.isFinite(value))
|
|
328
347
|
return value;
|
|
329
348
|
}
|
|
@@ -471,9 +490,9 @@ var require_eventLog = __commonJS({
|
|
|
471
490
|
exports.expandUserPath = expandUserPath;
|
|
472
491
|
exports.resolveEventLogPath = resolveEventLogPath;
|
|
473
492
|
exports.appendEventLog = appendEventLog;
|
|
474
|
-
var
|
|
493
|
+
var fs10 = __importStar(__require("fs"));
|
|
475
494
|
var os3 = __importStar(__require("os"));
|
|
476
|
-
var
|
|
495
|
+
var path10 = __importStar(__require("path"));
|
|
477
496
|
exports.EVENT_LOG_ENV_VAR = "ASCENDA_EVENT_LOG_FILE";
|
|
478
497
|
var MAX_BYTES = 5 * 1024 * 1024;
|
|
479
498
|
function expandUserPath(configured) {
|
|
@@ -483,8 +502,8 @@ var require_eventLog = __commonJS({
|
|
|
483
502
|
if (value === "~")
|
|
484
503
|
return os3.homedir();
|
|
485
504
|
if (value.startsWith("~/"))
|
|
486
|
-
return
|
|
487
|
-
return
|
|
505
|
+
return path10.join(os3.homedir(), value.slice(2));
|
|
506
|
+
return path10.resolve(value);
|
|
488
507
|
}
|
|
489
508
|
function resolveEventLogPath() {
|
|
490
509
|
return expandUserPath(process.env[exports.EVENT_LOG_ENV_VAR]);
|
|
@@ -492,20 +511,20 @@ var require_eventLog = __commonJS({
|
|
|
492
511
|
function appendEventLog(logFilePath, entry) {
|
|
493
512
|
try {
|
|
494
513
|
rotateIfLarge(logFilePath);
|
|
495
|
-
const dir =
|
|
496
|
-
|
|
497
|
-
|
|
514
|
+
const dir = path10.dirname(logFilePath);
|
|
515
|
+
fs10.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
516
|
+
fs10.appendFileSync(logFilePath, `${JSON.stringify(entry)}
|
|
498
517
|
`, { encoding: "utf8", mode: 384 });
|
|
499
518
|
if (process.platform !== "win32")
|
|
500
|
-
|
|
519
|
+
fs10.chmodSync(logFilePath, 384);
|
|
501
520
|
} catch {
|
|
502
521
|
}
|
|
503
522
|
}
|
|
504
523
|
function rotateIfLarge(logFilePath) {
|
|
505
524
|
try {
|
|
506
|
-
if (
|
|
525
|
+
if (fs10.statSync(logFilePath).size < MAX_BYTES)
|
|
507
526
|
return;
|
|
508
|
-
|
|
527
|
+
fs10.renameSync(logFilePath, `${logFilePath}.1`);
|
|
509
528
|
} catch {
|
|
510
529
|
}
|
|
511
530
|
}
|
|
@@ -660,26 +679,26 @@ var require_tokenStore = __commonJS({
|
|
|
660
679
|
exports.persistEventWriteToken = persistEventWriteToken;
|
|
661
680
|
exports.readTokenFile = readTokenFile2;
|
|
662
681
|
exports.sanitizeFilePart = sanitizeFilePart;
|
|
663
|
-
var
|
|
682
|
+
var fs10 = __importStar(__require("fs"));
|
|
664
683
|
var os3 = __importStar(__require("os"));
|
|
665
|
-
var
|
|
684
|
+
var path10 = __importStar(__require("path"));
|
|
666
685
|
function defaultTokenFilePath2(toolInstallationId) {
|
|
667
|
-
return
|
|
686
|
+
return path10.join(os3.homedir(), ".ascenda", "tokens", sanitizeFilePart(toolInstallationId));
|
|
668
687
|
}
|
|
669
688
|
function persistEventWriteToken(tokenFilePath, token) {
|
|
670
|
-
const dir =
|
|
671
|
-
|
|
672
|
-
|
|
689
|
+
const dir = path10.dirname(tokenFilePath);
|
|
690
|
+
fs10.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
691
|
+
fs10.writeFileSync(tokenFilePath, token, { encoding: "utf8", mode: 384 });
|
|
673
692
|
if (process.platform !== "win32") {
|
|
674
|
-
|
|
675
|
-
|
|
693
|
+
fs10.chmodSync(dir, 448);
|
|
694
|
+
fs10.chmodSync(tokenFilePath, 384);
|
|
676
695
|
}
|
|
677
696
|
}
|
|
678
697
|
function readTokenFile2(tokenFilePath) {
|
|
679
698
|
try {
|
|
680
|
-
if (!
|
|
699
|
+
if (!fs10.existsSync(tokenFilePath))
|
|
681
700
|
return void 0;
|
|
682
|
-
const value =
|
|
701
|
+
const value = fs10.readFileSync(tokenFilePath, "utf8").trim();
|
|
683
702
|
return value || void 0;
|
|
684
703
|
} catch {
|
|
685
704
|
return void 0;
|
|
@@ -738,20 +757,20 @@ var require_stateStore = __commonJS({
|
|
|
738
757
|
exports.recordSendOutcome = recordSendOutcome;
|
|
739
758
|
exports.shouldAnnounceFailure = shouldAnnounceFailure;
|
|
740
759
|
exports.markFailureNotified = markFailureNotified;
|
|
741
|
-
var
|
|
760
|
+
var fs10 = __importStar(__require("fs"));
|
|
742
761
|
var os3 = __importStar(__require("os"));
|
|
743
|
-
var
|
|
762
|
+
var path10 = __importStar(__require("path"));
|
|
744
763
|
var tokenStore_1 = require_tokenStore();
|
|
745
764
|
function defaultStateFilePath(toolInstallationId) {
|
|
746
765
|
const dir = process.env.ASCENDA_STATE_DIR?.trim();
|
|
747
|
-
const base = dir ? dir :
|
|
748
|
-
return
|
|
766
|
+
const base = dir ? dir : path10.join(os3.homedir(), ".ascenda", "state");
|
|
767
|
+
return path10.join(base, `${(0, tokenStore_1.sanitizeFilePart)(toolInstallationId)}.json`);
|
|
749
768
|
}
|
|
750
769
|
function readCollectorState(stateFilePath) {
|
|
751
770
|
try {
|
|
752
|
-
if (!
|
|
771
|
+
if (!fs10.existsSync(stateFilePath))
|
|
753
772
|
return void 0;
|
|
754
|
-
const raw =
|
|
773
|
+
const raw = fs10.readFileSync(stateFilePath, "utf8").trim();
|
|
755
774
|
if (!raw)
|
|
756
775
|
return void 0;
|
|
757
776
|
const parsed = JSON.parse(raw);
|
|
@@ -797,17 +816,17 @@ var require_stateStore = __commonJS({
|
|
|
797
816
|
function writeStateFile(stateFilePath, state) {
|
|
798
817
|
const temporaryPath = `${stateFilePath}.${process.pid}.tmp`;
|
|
799
818
|
try {
|
|
800
|
-
const dir =
|
|
801
|
-
|
|
802
|
-
|
|
819
|
+
const dir = path10.dirname(stateFilePath);
|
|
820
|
+
fs10.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
821
|
+
fs10.writeFileSync(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
803
822
|
`, { encoding: "utf8", mode: 384 });
|
|
804
|
-
|
|
823
|
+
fs10.renameSync(temporaryPath, stateFilePath);
|
|
805
824
|
if (process.platform !== "win32")
|
|
806
|
-
|
|
825
|
+
fs10.chmodSync(stateFilePath, 384);
|
|
807
826
|
} catch {
|
|
808
827
|
try {
|
|
809
|
-
if (
|
|
810
|
-
|
|
828
|
+
if (fs10.existsSync(temporaryPath))
|
|
829
|
+
fs10.unlinkSync(temporaryPath);
|
|
811
830
|
} catch {
|
|
812
831
|
}
|
|
813
832
|
}
|
|
@@ -847,6 +866,7 @@ var require_eventSender = __commonJS({
|
|
|
847
866
|
utcOffsetMinutes: (0, afterHours_1.utcOffsetMinutesAt)(/* @__PURE__ */ new Date()),
|
|
848
867
|
sessionId: identity.sessionId ?? void 0,
|
|
849
868
|
workspaceHash: identity.workspaceHash ?? void 0,
|
|
869
|
+
projectHash: identity.projectHash ?? void 0,
|
|
850
870
|
consentScope: tool_contract_1.ASCENDA_CONSENT_SCOPE,
|
|
851
871
|
provenance: tool_contract_1.ASCENDA_PROVENANCE,
|
|
852
872
|
privacyMode: "metadata_only",
|
|
@@ -902,6 +922,7 @@ var require_eventSender = __commonJS({
|
|
|
902
922
|
severity: "low",
|
|
903
923
|
sessionId: this.config.sessionId ?? void 0,
|
|
904
924
|
workspaceHash: this.config.workspaceHash ?? void 0,
|
|
925
|
+
projectHash: this.config.projectHash ?? void 0,
|
|
905
926
|
consentScope: tool_contract_1.ASCENDA_SEMANTIC_CONSENT_SCOPE,
|
|
906
927
|
provenance: tool_contract_1.ASCENDA_SEMANTIC_PROVENANCE,
|
|
907
928
|
privacyMode: "metadata_only",
|
|
@@ -931,6 +952,7 @@ var require_eventSender = __commonJS({
|
|
|
931
952
|
severity: "low",
|
|
932
953
|
sessionId: this.config.sessionId ?? void 0,
|
|
933
954
|
workspaceHash: this.config.workspaceHash ?? void 0,
|
|
955
|
+
projectHash: this.config.projectHash ?? void 0,
|
|
934
956
|
consentScope: tool_contract_1.ASCENDA_COLLABORATION_CONSENT_SCOPE,
|
|
935
957
|
provenance: tool_contract_1.ASCENDA_PROVENANCE,
|
|
936
958
|
privacyMode: "metadata_only",
|
|
@@ -970,7 +992,7 @@ var require_eventSender = __commonJS({
|
|
|
970
992
|
return (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
971
993
|
}
|
|
972
994
|
if (outcome.result === "transport_error" && (outcome.httpStatus === void 0 || (0, http_1.isRetryableStatus)(outcome.httpStatus))) {
|
|
973
|
-
await new Promise((
|
|
995
|
+
await new Promise((resolve2) => setTimeout(resolve2, RETRY_DELAY_MS));
|
|
974
996
|
return (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
975
997
|
}
|
|
976
998
|
return outcome;
|
|
@@ -1021,19 +1043,387 @@ var require_eventSender = __commonJS({
|
|
|
1021
1043
|
}
|
|
1022
1044
|
});
|
|
1023
1045
|
|
|
1046
|
+
// ../packages/tool-kit/out/contextRegistry.js
|
|
1047
|
+
var require_contextRegistry = __commonJS({
|
|
1048
|
+
"../packages/tool-kit/out/contextRegistry.js"(exports) {
|
|
1049
|
+
"use strict";
|
|
1050
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1051
|
+
if (k2 === void 0) k2 = k;
|
|
1052
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1053
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1054
|
+
desc = { enumerable: true, get: function() {
|
|
1055
|
+
return m[k];
|
|
1056
|
+
} };
|
|
1057
|
+
}
|
|
1058
|
+
Object.defineProperty(o, k2, desc);
|
|
1059
|
+
} : function(o, m, k, k2) {
|
|
1060
|
+
if (k2 === void 0) k2 = k;
|
|
1061
|
+
o[k2] = m[k];
|
|
1062
|
+
});
|
|
1063
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
1064
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1065
|
+
} : function(o, v) {
|
|
1066
|
+
o["default"] = v;
|
|
1067
|
+
});
|
|
1068
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
1069
|
+
var ownKeys = function(o) {
|
|
1070
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1071
|
+
var ar = [];
|
|
1072
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1073
|
+
return ar;
|
|
1074
|
+
};
|
|
1075
|
+
return ownKeys(o);
|
|
1076
|
+
};
|
|
1077
|
+
return function(mod) {
|
|
1078
|
+
if (mod && mod.__esModule) return mod;
|
|
1079
|
+
var result = {};
|
|
1080
|
+
if (mod != null) {
|
|
1081
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1082
|
+
}
|
|
1083
|
+
__setModuleDefault(result, mod);
|
|
1084
|
+
return result;
|
|
1085
|
+
};
|
|
1086
|
+
}();
|
|
1087
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1088
|
+
exports.workContextRegistryFilePath = workContextRegistryFilePath;
|
|
1089
|
+
exports.readWorkContextRegistry = readWorkContextRegistry;
|
|
1090
|
+
exports.recordWorkContext = recordWorkContext2;
|
|
1091
|
+
exports.recordWorkContextAlias = recordWorkContextAlias2;
|
|
1092
|
+
var fs10 = __importStar(__require("fs"));
|
|
1093
|
+
var os3 = __importStar(__require("os"));
|
|
1094
|
+
var path10 = __importStar(__require("path"));
|
|
1095
|
+
var MAX_PATHS_PER_ENTRY = 8;
|
|
1096
|
+
function workContextRegistryFilePath() {
|
|
1097
|
+
return path10.join(os3.homedir(), ".ascenda", "work-contexts.json");
|
|
1098
|
+
}
|
|
1099
|
+
function readWorkContextRegistry(registryFilePath = workContextRegistryFilePath()) {
|
|
1100
|
+
try {
|
|
1101
|
+
const parsed = JSON.parse(fs10.readFileSync(registryFilePath, "utf8"));
|
|
1102
|
+
if (parsed && parsed.version === 1 && parsed.contexts && typeof parsed.contexts === "object") {
|
|
1103
|
+
return parsed;
|
|
1104
|
+
}
|
|
1105
|
+
} catch {
|
|
1106
|
+
}
|
|
1107
|
+
return { version: 1, contexts: {} };
|
|
1108
|
+
}
|
|
1109
|
+
function recordWorkContext2(context, options) {
|
|
1110
|
+
if (!context)
|
|
1111
|
+
return false;
|
|
1112
|
+
const updates = [];
|
|
1113
|
+
if (context.projectHash && context.projectLabel) {
|
|
1114
|
+
updates.push({ hash: context.projectHash, kind: "project", label: context.projectLabel, observedPath: context.projectPath ?? context.workspacePath });
|
|
1115
|
+
}
|
|
1116
|
+
if (context.workspaceHash && context.workspaceLabel && context.workspaceHash !== context.projectHash) {
|
|
1117
|
+
updates.push({ hash: context.workspaceHash, kind: "workspace", label: context.workspaceLabel, observedPath: context.workspacePath });
|
|
1118
|
+
}
|
|
1119
|
+
return upsert(updates, options);
|
|
1120
|
+
}
|
|
1121
|
+
function recordWorkContextAlias2(hash, label, observedPath, options) {
|
|
1122
|
+
if (!hash || !label)
|
|
1123
|
+
return false;
|
|
1124
|
+
return upsert([{ hash, kind: "alias", label, observedPath: observedPath ?? null }], options);
|
|
1125
|
+
}
|
|
1126
|
+
function upsert(updates, options) {
|
|
1127
|
+
if (updates.length === 0)
|
|
1128
|
+
return false;
|
|
1129
|
+
try {
|
|
1130
|
+
const registryFilePath = options?.registryFilePath ?? workContextRegistryFilePath();
|
|
1131
|
+
const nowIso = (options?.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
1132
|
+
const registry = readWorkContextRegistry(registryFilePath);
|
|
1133
|
+
let dirty = false;
|
|
1134
|
+
for (const update of updates) {
|
|
1135
|
+
const existing = registry.contexts[update.hash];
|
|
1136
|
+
if (!existing) {
|
|
1137
|
+
registry.contexts[update.hash] = {
|
|
1138
|
+
kind: update.kind,
|
|
1139
|
+
label: update.label,
|
|
1140
|
+
paths: update.observedPath ? [update.observedPath] : [],
|
|
1141
|
+
firstSeenAt: nowIso,
|
|
1142
|
+
lastSeenAt: nowIso
|
|
1143
|
+
};
|
|
1144
|
+
dirty = true;
|
|
1145
|
+
continue;
|
|
1146
|
+
}
|
|
1147
|
+
if (existing.kind === "alias" && update.kind !== "alias") {
|
|
1148
|
+
existing.kind = update.kind;
|
|
1149
|
+
dirty = true;
|
|
1150
|
+
}
|
|
1151
|
+
if (existing.label !== update.label && update.kind !== "alias") {
|
|
1152
|
+
existing.label = update.label;
|
|
1153
|
+
dirty = true;
|
|
1154
|
+
}
|
|
1155
|
+
if (update.observedPath && !existing.paths.includes(update.observedPath)) {
|
|
1156
|
+
if (existing.paths.length < MAX_PATHS_PER_ENTRY)
|
|
1157
|
+
existing.paths.push(update.observedPath);
|
|
1158
|
+
dirty = true;
|
|
1159
|
+
}
|
|
1160
|
+
if (dayOf(existing.lastSeenAt) !== dayOf(nowIso)) {
|
|
1161
|
+
existing.lastSeenAt = nowIso;
|
|
1162
|
+
dirty = true;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
if (!dirty)
|
|
1166
|
+
return false;
|
|
1167
|
+
writeRegistry(registryFilePath, registry);
|
|
1168
|
+
return true;
|
|
1169
|
+
} catch {
|
|
1170
|
+
return false;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
function dayOf(iso) {
|
|
1174
|
+
return iso.slice(0, 10);
|
|
1175
|
+
}
|
|
1176
|
+
function writeRegistry(registryFilePath, registry) {
|
|
1177
|
+
const dir = path10.dirname(registryFilePath);
|
|
1178
|
+
fs10.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1179
|
+
const tmp = `${registryFilePath}.${process.pid}.tmp`;
|
|
1180
|
+
fs10.writeFileSync(tmp, `${JSON.stringify(registry, null, 2)}
|
|
1181
|
+
`, { encoding: "utf8", mode: 384 });
|
|
1182
|
+
fs10.renameSync(tmp, registryFilePath);
|
|
1183
|
+
if (process.platform !== "win32") {
|
|
1184
|
+
fs10.chmodSync(registryFilePath, 384);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
// ../packages/tool-kit/out/salt.js
|
|
1191
|
+
var require_salt = __commonJS({
|
|
1192
|
+
"../packages/tool-kit/out/salt.js"(exports) {
|
|
1193
|
+
"use strict";
|
|
1194
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1195
|
+
if (k2 === void 0) k2 = k;
|
|
1196
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1197
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1198
|
+
desc = { enumerable: true, get: function() {
|
|
1199
|
+
return m[k];
|
|
1200
|
+
} };
|
|
1201
|
+
}
|
|
1202
|
+
Object.defineProperty(o, k2, desc);
|
|
1203
|
+
} : function(o, m, k, k2) {
|
|
1204
|
+
if (k2 === void 0) k2 = k;
|
|
1205
|
+
o[k2] = m[k];
|
|
1206
|
+
});
|
|
1207
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
1208
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1209
|
+
} : function(o, v) {
|
|
1210
|
+
o["default"] = v;
|
|
1211
|
+
});
|
|
1212
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
1213
|
+
var ownKeys = function(o) {
|
|
1214
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1215
|
+
var ar = [];
|
|
1216
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1217
|
+
return ar;
|
|
1218
|
+
};
|
|
1219
|
+
return ownKeys(o);
|
|
1220
|
+
};
|
|
1221
|
+
return function(mod) {
|
|
1222
|
+
if (mod && mod.__esModule) return mod;
|
|
1223
|
+
var result = {};
|
|
1224
|
+
if (mod != null) {
|
|
1225
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1226
|
+
}
|
|
1227
|
+
__setModuleDefault(result, mod);
|
|
1228
|
+
return result;
|
|
1229
|
+
};
|
|
1230
|
+
}();
|
|
1231
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1232
|
+
exports.machineSaltFilePath = machineSaltFilePath;
|
|
1233
|
+
exports.readOrCreateMachineSalt = readOrCreateMachineSalt;
|
|
1234
|
+
exports.hashWithMachineSalt = hashWithMachineSalt2;
|
|
1235
|
+
var crypto = __importStar(__require("crypto"));
|
|
1236
|
+
var fs10 = __importStar(__require("fs"));
|
|
1237
|
+
var os3 = __importStar(__require("os"));
|
|
1238
|
+
var path10 = __importStar(__require("path"));
|
|
1239
|
+
function machineSaltFilePath() {
|
|
1240
|
+
return path10.join(os3.homedir(), ".ascenda", "salt");
|
|
1241
|
+
}
|
|
1242
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1243
|
+
function readOrCreateMachineSalt(saltFilePath = machineSaltFilePath()) {
|
|
1244
|
+
const hit = cache.get(saltFilePath);
|
|
1245
|
+
if (hit)
|
|
1246
|
+
return hit;
|
|
1247
|
+
const dir = path10.dirname(saltFilePath);
|
|
1248
|
+
fs10.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1249
|
+
let salt;
|
|
1250
|
+
try {
|
|
1251
|
+
salt = crypto.randomBytes(32).toString("hex");
|
|
1252
|
+
fs10.writeFileSync(saltFilePath, salt, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
if (error.code !== "EEXIST")
|
|
1255
|
+
throw error;
|
|
1256
|
+
salt = fs10.readFileSync(saltFilePath, "utf8").trim();
|
|
1257
|
+
}
|
|
1258
|
+
if (process.platform !== "win32") {
|
|
1259
|
+
fs10.chmodSync(dir, 448);
|
|
1260
|
+
fs10.chmodSync(saltFilePath, 384);
|
|
1261
|
+
}
|
|
1262
|
+
cache.set(saltFilePath, salt);
|
|
1263
|
+
return salt;
|
|
1264
|
+
}
|
|
1265
|
+
function hashWithMachineSalt2(value, saltFilePath) {
|
|
1266
|
+
if (!value)
|
|
1267
|
+
return null;
|
|
1268
|
+
return crypto.createHash("sha256").update(readOrCreateMachineSalt(saltFilePath)).update("\0").update(value).digest("hex").slice(0, 16);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
// ../packages/tool-kit/out/workContext.js
|
|
1274
|
+
var require_workContext = __commonJS({
|
|
1275
|
+
"../packages/tool-kit/out/workContext.js"(exports) {
|
|
1276
|
+
"use strict";
|
|
1277
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1278
|
+
if (k2 === void 0) k2 = k;
|
|
1279
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1280
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1281
|
+
desc = { enumerable: true, get: function() {
|
|
1282
|
+
return m[k];
|
|
1283
|
+
} };
|
|
1284
|
+
}
|
|
1285
|
+
Object.defineProperty(o, k2, desc);
|
|
1286
|
+
} : function(o, m, k, k2) {
|
|
1287
|
+
if (k2 === void 0) k2 = k;
|
|
1288
|
+
o[k2] = m[k];
|
|
1289
|
+
});
|
|
1290
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
1291
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1292
|
+
} : function(o, v) {
|
|
1293
|
+
o["default"] = v;
|
|
1294
|
+
});
|
|
1295
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
1296
|
+
var ownKeys = function(o) {
|
|
1297
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1298
|
+
var ar = [];
|
|
1299
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1300
|
+
return ar;
|
|
1301
|
+
};
|
|
1302
|
+
return ownKeys(o);
|
|
1303
|
+
};
|
|
1304
|
+
return function(mod) {
|
|
1305
|
+
if (mod && mod.__esModule) return mod;
|
|
1306
|
+
var result = {};
|
|
1307
|
+
if (mod != null) {
|
|
1308
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1309
|
+
}
|
|
1310
|
+
__setModuleDefault(result, mod);
|
|
1311
|
+
return result;
|
|
1312
|
+
};
|
|
1313
|
+
}();
|
|
1314
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1315
|
+
exports.deriveWorkContext = deriveWorkContext2;
|
|
1316
|
+
var fs10 = __importStar(__require("fs"));
|
|
1317
|
+
var path10 = __importStar(__require("path"));
|
|
1318
|
+
var salt_1 = require_salt();
|
|
1319
|
+
var MAX_WALK_DEPTH = 64;
|
|
1320
|
+
function deriveWorkContext2(cwd, saltFilePath) {
|
|
1321
|
+
if (!cwd || !cwd.trim())
|
|
1322
|
+
return null;
|
|
1323
|
+
const startPath = stripTrailingSeparators(cwd.trim());
|
|
1324
|
+
let roots = null;
|
|
1325
|
+
try {
|
|
1326
|
+
roots = resolveRepositoryRoots(startPath);
|
|
1327
|
+
} catch {
|
|
1328
|
+
roots = null;
|
|
1329
|
+
}
|
|
1330
|
+
const workspacePath = roots?.checkoutRoot ?? startPath;
|
|
1331
|
+
const workspaceLabel = basenameOf(workspacePath);
|
|
1332
|
+
if (!workspaceLabel)
|
|
1333
|
+
return null;
|
|
1334
|
+
const projectPath = roots?.canonicalRoot ?? null;
|
|
1335
|
+
const projectLabel = (projectPath ? basenameOf(projectPath) : null) ?? workspaceLabel;
|
|
1336
|
+
return {
|
|
1337
|
+
workspaceLabel,
|
|
1338
|
+
projectLabel,
|
|
1339
|
+
workspaceHash: (0, salt_1.hashWithMachineSalt)(workspaceLabel, saltFilePath),
|
|
1340
|
+
projectHash: (0, salt_1.hashWithMachineSalt)(projectLabel, saltFilePath),
|
|
1341
|
+
workspacePath,
|
|
1342
|
+
projectPath
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
function resolveRepositoryRoots(startDir) {
|
|
1346
|
+
if (!fs10.existsSync(startDir))
|
|
1347
|
+
return null;
|
|
1348
|
+
let dir = startDir;
|
|
1349
|
+
for (let depth = 0; depth < MAX_WALK_DEPTH; depth++) {
|
|
1350
|
+
const dotGit = path10.join(dir, ".git");
|
|
1351
|
+
let stat6 = null;
|
|
1352
|
+
try {
|
|
1353
|
+
stat6 = fs10.statSync(dotGit);
|
|
1354
|
+
} catch {
|
|
1355
|
+
stat6 = null;
|
|
1356
|
+
}
|
|
1357
|
+
if (stat6?.isDirectory())
|
|
1358
|
+
return { checkoutRoot: dir, canonicalRoot: dir };
|
|
1359
|
+
if (stat6?.isFile())
|
|
1360
|
+
return { checkoutRoot: dir, canonicalRoot: worktreeParentRoot(dotGit, dir) ?? dir };
|
|
1361
|
+
const parent = path10.dirname(dir);
|
|
1362
|
+
if (parent === dir)
|
|
1363
|
+
return null;
|
|
1364
|
+
dir = parent;
|
|
1365
|
+
}
|
|
1366
|
+
return null;
|
|
1367
|
+
}
|
|
1368
|
+
function worktreeParentRoot(dotGitFile, containingDir) {
|
|
1369
|
+
let gitdir;
|
|
1370
|
+
try {
|
|
1371
|
+
const match = /^gitdir:\s*(.+)\s*$/m.exec(fs10.readFileSync(dotGitFile, "utf8"));
|
|
1372
|
+
if (!match)
|
|
1373
|
+
return null;
|
|
1374
|
+
gitdir = match[1].trim();
|
|
1375
|
+
} catch {
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1378
|
+
const resolved = path10.resolve(containingDir, gitdir);
|
|
1379
|
+
const marker = `${path10.sep}.git${path10.sep}worktrees${path10.sep}`;
|
|
1380
|
+
const idx = resolved.indexOf(marker);
|
|
1381
|
+
if (idx === -1)
|
|
1382
|
+
return null;
|
|
1383
|
+
return resolved.slice(0, idx);
|
|
1384
|
+
}
|
|
1385
|
+
function stripTrailingSeparators(value) {
|
|
1386
|
+
let end = value.length;
|
|
1387
|
+
while (end > 1 && (value[end - 1] === "/" || value[end - 1] === "\\"))
|
|
1388
|
+
end--;
|
|
1389
|
+
return value.slice(0, end);
|
|
1390
|
+
}
|
|
1391
|
+
function basenameOf(value) {
|
|
1392
|
+
const segment = value.split(/[\\/]/).filter(Boolean).pop() ?? null;
|
|
1393
|
+
return segment && segment.length > 0 ? segment : null;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1024
1398
|
// ../packages/tool-kit/out/hookAdapter.js
|
|
1025
1399
|
var require_hookAdapter = __commonJS({
|
|
1026
1400
|
"../packages/tool-kit/out/hookAdapter.js"(exports) {
|
|
1027
1401
|
"use strict";
|
|
1028
1402
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1029
1403
|
exports.DEFAULT_API_BASE_URL = void 0;
|
|
1404
|
+
exports.resolveContextHashes = resolveContextHashes;
|
|
1030
1405
|
exports.loadCliAgentConfig = loadCliAgentConfig;
|
|
1031
1406
|
exports.deliverHookEvents = deliverHookEvents;
|
|
1407
|
+
var contextRegistry_1 = require_contextRegistry();
|
|
1032
1408
|
var eventLog_1 = require_eventLog();
|
|
1033
1409
|
var eventSender_1 = require_eventSender();
|
|
1034
1410
|
var tokenStore_1 = require_tokenStore();
|
|
1411
|
+
var workContext_1 = require_workContext();
|
|
1035
1412
|
exports.DEFAULT_API_BASE_URL = "https://api.ascenda.one";
|
|
1036
|
-
function
|
|
1413
|
+
function resolveContextHashes(cwd) {
|
|
1414
|
+
const workspaceOverride = process.env.ASCENDA_WORKSPACE_HASH?.trim() || null;
|
|
1415
|
+
const projectOverride = process.env.ASCENDA_PROJECT_HASH?.trim() || null;
|
|
1416
|
+
if (workspaceOverride && projectOverride)
|
|
1417
|
+
return { workspaceHash: workspaceOverride, projectHash: projectOverride };
|
|
1418
|
+
const context = (0, workContext_1.deriveWorkContext)(cwd ?? process.cwd());
|
|
1419
|
+
if (context)
|
|
1420
|
+
(0, contextRegistry_1.recordWorkContext)(context);
|
|
1421
|
+
return {
|
|
1422
|
+
workspaceHash: workspaceOverride ?? context?.workspaceHash ?? null,
|
|
1423
|
+
projectHash: projectOverride ?? context?.projectHash ?? null
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
function loadCliAgentConfig(toolType, sessionIdFromHook, cwd) {
|
|
1037
1427
|
const apiBaseUrl = (process.env.ASCENDA_API_BASE_URL ?? exports.DEFAULT_API_BASE_URL).replace(/\/$/, "");
|
|
1038
1428
|
const toolInstallationIdRaw = process.env.ASCENDA_TOOL_INSTALLATION_ID;
|
|
1039
1429
|
if (!toolInstallationIdRaw)
|
|
@@ -1046,13 +1436,15 @@ var require_hookAdapter = __commonJS({
|
|
|
1046
1436
|
throw new Error("Missing ASCENDA_EVENT_WRITE_TOKEN (or token file)");
|
|
1047
1437
|
if (!fileToken)
|
|
1048
1438
|
(0, tokenStore_1.persistEventWriteToken)(tokenFilePath, eventWriteToken);
|
|
1439
|
+
const contextHashes = resolveContextHashes(cwd);
|
|
1049
1440
|
return {
|
|
1050
1441
|
apiBaseUrl,
|
|
1051
1442
|
toolInstallationId,
|
|
1052
1443
|
eventWriteToken,
|
|
1053
1444
|
tokenFilePath,
|
|
1054
1445
|
sessionId: process.env.ASCENDA_SESSION_ID ?? sessionIdFromHook ?? null,
|
|
1055
|
-
workspaceHash:
|
|
1446
|
+
workspaceHash: contextHashes.workspaceHash,
|
|
1447
|
+
projectHash: contextHashes.projectHash,
|
|
1056
1448
|
// Agents await command hooks; fail fast rather than stall the user's turn.
|
|
1057
1449
|
timeoutMs: parsePositiveInt(process.env.ASCENDA_HTTP_TIMEOUT_MS) ?? 3e3
|
|
1058
1450
|
};
|
|
@@ -1063,11 +1455,12 @@ var require_hookAdapter = __commonJS({
|
|
|
1063
1455
|
const notice = options.onNotice ?? ((message) => console.error(message));
|
|
1064
1456
|
let config;
|
|
1065
1457
|
try {
|
|
1066
|
-
config = loadCliAgentConfig(options.toolType, options.sessionId);
|
|
1458
|
+
config = loadCliAgentConfig(options.toolType, options.sessionId, options.cwd);
|
|
1067
1459
|
} catch (error) {
|
|
1068
1460
|
const logFile = (0, eventLog_1.resolveEventLogPath)();
|
|
1069
1461
|
if (!logFile)
|
|
1070
1462
|
throw error;
|
|
1463
|
+
const contextHashes = resolveContextHashes(options.cwd);
|
|
1071
1464
|
for (const event of events) {
|
|
1072
1465
|
(0, eventLog_1.appendEventLog)(logFile, {
|
|
1073
1466
|
loggedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1076,7 +1469,8 @@ var require_hookAdapter = __commonJS({
|
|
|
1076
1469
|
toolInstallationId: `${options.toolType}:unpaired`,
|
|
1077
1470
|
source: options.source,
|
|
1078
1471
|
sessionId: options.sessionId ?? null,
|
|
1079
|
-
workspaceHash:
|
|
1472
|
+
workspaceHash: contextHashes.workspaceHash,
|
|
1473
|
+
projectHash: contextHashes.projectHash
|
|
1080
1474
|
}, event)
|
|
1081
1475
|
});
|
|
1082
1476
|
}
|
|
@@ -1090,6 +1484,7 @@ var require_hookAdapter = __commonJS({
|
|
|
1090
1484
|
tokenFilePath: config.tokenFilePath,
|
|
1091
1485
|
sessionId: config.sessionId,
|
|
1092
1486
|
workspaceHash: config.workspaceHash,
|
|
1487
|
+
projectHash: config.projectHash,
|
|
1093
1488
|
timeoutMs: config.timeoutMs
|
|
1094
1489
|
});
|
|
1095
1490
|
for (const event of events) {
|
|
@@ -1159,12 +1554,12 @@ var require_turnState = __commonJS({
|
|
|
1159
1554
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1160
1555
|
exports.recordTurnStart = recordTurnStart;
|
|
1161
1556
|
exports.consumeTurnDurationMs = consumeTurnDurationMs;
|
|
1162
|
-
var
|
|
1557
|
+
var fs10 = __importStar(__require("fs"));
|
|
1163
1558
|
var os3 = __importStar(__require("os"));
|
|
1164
|
-
var
|
|
1165
|
-
var stateDir = () => process.env.ASCENDA_STATE_DIR ??
|
|
1559
|
+
var path10 = __importStar(__require("path"));
|
|
1560
|
+
var stateDir = () => process.env.ASCENDA_STATE_DIR ?? path10.join(os3.homedir(), ".ascenda", "state");
|
|
1166
1561
|
function turnFile(agent, sessionId) {
|
|
1167
|
-
return
|
|
1562
|
+
return path10.join(stateDir(), `${sanitize(agent)}-turn-${sanitize(sessionId)}`);
|
|
1168
1563
|
}
|
|
1169
1564
|
function sanitize(value) {
|
|
1170
1565
|
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
@@ -1173,8 +1568,8 @@ var require_turnState = __commonJS({
|
|
|
1173
1568
|
if (!sessionId)
|
|
1174
1569
|
return;
|
|
1175
1570
|
try {
|
|
1176
|
-
|
|
1177
|
-
|
|
1571
|
+
fs10.mkdirSync(stateDir(), { recursive: true });
|
|
1572
|
+
fs10.writeFileSync(turnFile(agent, sessionId), String(now), { encoding: "utf8", mode: 384 });
|
|
1178
1573
|
} catch {
|
|
1179
1574
|
}
|
|
1180
1575
|
}
|
|
@@ -1183,8 +1578,8 @@ var require_turnState = __commonJS({
|
|
|
1183
1578
|
return void 0;
|
|
1184
1579
|
const file = turnFile(agent, sessionId);
|
|
1185
1580
|
try {
|
|
1186
|
-
const started = Number(
|
|
1187
|
-
|
|
1581
|
+
const started = Number(fs10.readFileSync(file, "utf8").trim());
|
|
1582
|
+
fs10.rmSync(file, { force: true });
|
|
1188
1583
|
if (!Number.isFinite(started) || started <= 0 || started > now)
|
|
1189
1584
|
return void 0;
|
|
1190
1585
|
return now - started;
|
|
@@ -1195,9 +1590,9 @@ var require_turnState = __commonJS({
|
|
|
1195
1590
|
}
|
|
1196
1591
|
});
|
|
1197
1592
|
|
|
1198
|
-
// ../packages/tool-kit/out/
|
|
1199
|
-
var
|
|
1200
|
-
"../packages/tool-kit/out/
|
|
1593
|
+
// ../packages/tool-kit/out/liveBus.js
|
|
1594
|
+
var require_liveBus = __commonJS({
|
|
1595
|
+
"../packages/tool-kit/out/liveBus.js"(exports) {
|
|
1201
1596
|
"use strict";
|
|
1202
1597
|
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1203
1598
|
if (k2 === void 0) k2 = k;
|
|
@@ -1237,114 +1632,31 @@ var require_salt = __commonJS({
|
|
|
1237
1632
|
};
|
|
1238
1633
|
}();
|
|
1239
1634
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1240
|
-
exports.
|
|
1241
|
-
exports.
|
|
1242
|
-
exports.
|
|
1243
|
-
|
|
1244
|
-
var
|
|
1635
|
+
exports.liveBusSocketCandidates = liveBusSocketCandidates;
|
|
1636
|
+
exports.liveBusSocketPath = liveBusSocketPath;
|
|
1637
|
+
exports.bucketPromptSize = bucketPromptSize;
|
|
1638
|
+
exports.emitLiveSignal = emitLiveSignal;
|
|
1639
|
+
var fs10 = __importStar(__require("fs"));
|
|
1640
|
+
var net = __importStar(__require("net"));
|
|
1245
1641
|
var os3 = __importStar(__require("os"));
|
|
1246
|
-
var
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
try {
|
|
1259
|
-
salt = crypto.randomBytes(32).toString("hex");
|
|
1260
|
-
fs9.writeFileSync(saltFilePath, salt, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
1261
|
-
} catch (error) {
|
|
1262
|
-
if (error.code !== "EEXIST")
|
|
1263
|
-
throw error;
|
|
1264
|
-
salt = fs9.readFileSync(saltFilePath, "utf8").trim();
|
|
1265
|
-
}
|
|
1266
|
-
if (process.platform !== "win32") {
|
|
1267
|
-
fs9.chmodSync(dir, 448);
|
|
1268
|
-
fs9.chmodSync(saltFilePath, 384);
|
|
1269
|
-
}
|
|
1270
|
-
cache.set(saltFilePath, salt);
|
|
1271
|
-
return salt;
|
|
1272
|
-
}
|
|
1273
|
-
function hashWithMachineSalt2(value, saltFilePath) {
|
|
1274
|
-
if (!value)
|
|
1275
|
-
return null;
|
|
1276
|
-
return crypto.createHash("sha256").update(readOrCreateMachineSalt(saltFilePath)).update("\0").update(value).digest("hex").slice(0, 16);
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
});
|
|
1280
|
-
|
|
1281
|
-
// ../packages/tool-kit/out/liveBus.js
|
|
1282
|
-
var require_liveBus = __commonJS({
|
|
1283
|
-
"../packages/tool-kit/out/liveBus.js"(exports) {
|
|
1284
|
-
"use strict";
|
|
1285
|
-
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1286
|
-
if (k2 === void 0) k2 = k;
|
|
1287
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1288
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1289
|
-
desc = { enumerable: true, get: function() {
|
|
1290
|
-
return m[k];
|
|
1291
|
-
} };
|
|
1292
|
-
}
|
|
1293
|
-
Object.defineProperty(o, k2, desc);
|
|
1294
|
-
} : function(o, m, k, k2) {
|
|
1295
|
-
if (k2 === void 0) k2 = k;
|
|
1296
|
-
o[k2] = m[k];
|
|
1297
|
-
});
|
|
1298
|
-
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
1299
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1300
|
-
} : function(o, v) {
|
|
1301
|
-
o["default"] = v;
|
|
1302
|
-
});
|
|
1303
|
-
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
1304
|
-
var ownKeys = function(o) {
|
|
1305
|
-
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1306
|
-
var ar = [];
|
|
1307
|
-
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1308
|
-
return ar;
|
|
1309
|
-
};
|
|
1310
|
-
return ownKeys(o);
|
|
1311
|
-
};
|
|
1312
|
-
return function(mod) {
|
|
1313
|
-
if (mod && mod.__esModule) return mod;
|
|
1314
|
-
var result = {};
|
|
1315
|
-
if (mod != null) {
|
|
1316
|
-
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1317
|
-
}
|
|
1318
|
-
__setModuleDefault(result, mod);
|
|
1319
|
-
return result;
|
|
1320
|
-
};
|
|
1321
|
-
}();
|
|
1322
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1323
|
-
exports.liveBusSocketCandidates = liveBusSocketCandidates;
|
|
1324
|
-
exports.liveBusSocketPath = liveBusSocketPath;
|
|
1325
|
-
exports.bucketPromptSize = bucketPromptSize;
|
|
1326
|
-
exports.emitLiveSignal = emitLiveSignal;
|
|
1327
|
-
var fs9 = __importStar(__require("fs"));
|
|
1328
|
-
var net = __importStar(__require("net"));
|
|
1329
|
-
var os3 = __importStar(__require("os"));
|
|
1330
|
-
var path9 = __importStar(__require("path"));
|
|
1331
|
-
var WRITE_TIMEOUT_MS = 50;
|
|
1332
|
-
var APP_BUNDLE_ID2 = "one.ascenda.ascendaMissionControl";
|
|
1333
|
-
function liveBusSocketCandidates() {
|
|
1334
|
-
const override = process.env.ASCENDA_LIVE_BUS_SOCKET;
|
|
1335
|
-
if (override)
|
|
1336
|
-
return [override];
|
|
1337
|
-
const home = os3.homedir();
|
|
1338
|
-
return [
|
|
1339
|
-
path9.join(home, ".ascenda", "live.sock"),
|
|
1340
|
-
path9.join(home, "Library", "Containers", APP_BUNDLE_ID2, "Data", ".ascenda", "live.sock")
|
|
1341
|
-
];
|
|
1642
|
+
var path10 = __importStar(__require("path"));
|
|
1643
|
+
var WRITE_TIMEOUT_MS = 50;
|
|
1644
|
+
var APP_BUNDLE_ID2 = "one.ascenda.ascendaMissionControl";
|
|
1645
|
+
function liveBusSocketCandidates() {
|
|
1646
|
+
const override = process.env.ASCENDA_LIVE_BUS_SOCKET;
|
|
1647
|
+
if (override)
|
|
1648
|
+
return [override];
|
|
1649
|
+
const home = os3.homedir();
|
|
1650
|
+
return [
|
|
1651
|
+
path10.join(home, ".ascenda", "live.sock"),
|
|
1652
|
+
path10.join(home, "Library", "Containers", APP_BUNDLE_ID2, "Data", ".ascenda", "live.sock")
|
|
1653
|
+
];
|
|
1342
1654
|
}
|
|
1343
1655
|
function liveBusSocketPath() {
|
|
1344
1656
|
const candidates = liveBusSocketCandidates();
|
|
1345
1657
|
for (const candidate of candidates) {
|
|
1346
1658
|
try {
|
|
1347
|
-
if (
|
|
1659
|
+
if (fs10.statSync(candidate).isSocket())
|
|
1348
1660
|
return candidate;
|
|
1349
1661
|
} catch {
|
|
1350
1662
|
}
|
|
@@ -1362,7 +1674,7 @@ var require_liveBus = __commonJS({
|
|
|
1362
1674
|
return "xl";
|
|
1363
1675
|
}
|
|
1364
1676
|
function emitLiveSignal(signal) {
|
|
1365
|
-
return new Promise((
|
|
1677
|
+
return new Promise((resolve2) => {
|
|
1366
1678
|
let settled = false;
|
|
1367
1679
|
const done = () => {
|
|
1368
1680
|
if (settled)
|
|
@@ -1372,13 +1684,13 @@ var require_liveBus = __commonJS({
|
|
|
1372
1684
|
socket.destroy();
|
|
1373
1685
|
} catch {
|
|
1374
1686
|
}
|
|
1375
|
-
|
|
1687
|
+
resolve2();
|
|
1376
1688
|
};
|
|
1377
1689
|
let socket;
|
|
1378
1690
|
try {
|
|
1379
1691
|
socket = net.createConnection(liveBusSocketPath());
|
|
1380
1692
|
} catch {
|
|
1381
|
-
|
|
1693
|
+
resolve2();
|
|
1382
1694
|
return;
|
|
1383
1695
|
}
|
|
1384
1696
|
const timer = setTimeout(done, WRITE_TIMEOUT_MS);
|
|
@@ -1409,8 +1721,8 @@ var require_out2 = __commonJS({
|
|
|
1409
1721
|
"../packages/tool-kit/out/index.js"(exports) {
|
|
1410
1722
|
"use strict";
|
|
1411
1723
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1412
|
-
exports.
|
|
1413
|
-
exports.isRetryableStatus = exports.parseIngestResponse = exports.postToolEventsBatch = exports.postToolEvent = void 0;
|
|
1724
|
+
exports.workContextRegistryFilePath = exports.readWorkContextRegistry = exports.recordWorkContextAlias = exports.recordWorkContext = exports.deriveWorkContext = exports.hashWithMachineSalt = exports.readOrCreateMachineSalt = exports.machineSaltFilePath = exports.markFailureNotified = exports.shouldAnnounceFailure = exports.recordSendOutcome = exports.readCollectorState = exports.defaultStateFilePath = exports.readTokenFile = exports.persistEventWriteToken = exports.defaultTokenFilePath = exports.recordTurnStart = exports.consumeTurnDurationMs = exports.resolveContextHashes = exports.loadCliAgentConfig = exports.deliverHookEvents = exports.DEFAULT_API_BASE_URL = exports.resolveEventLogPath = exports.expandUserPath = exports.appendEventLog = exports.EVENT_LOG_ENV_VAR = exports.buildEventPayload = exports.AscendaSemanticEventError = exports.AscendaEventSender = exports.looksLikeCorrection = exports.outcomeForHook = exports.inferOutcome = exports.getNestedNumber = exports.getNestedString = exports.getNested = exports.getNumber = exports.getString = exports.localHourAt = exports.utcOffsetMinutesAt = exports.BUSINESS_DAY = exports.isOutsideBusinessHours = exports.isAfterHours = exports.bucketDurationMs = exports.bucketLinesChanged = exports.invitesDebrief = exports.classifyWorkMilestone = exports.isReworkGitAction = exports.classifyGitAction = exports.isVerificationCommand = exports.classifyCommand = void 0;
|
|
1725
|
+
exports.isRetryableStatus = exports.parseIngestResponse = exports.postToolEventsBatch = exports.postToolEvent = exports.renewToolToken = exports.getPairingStatus = exports.createPairingSession = exports.AscendaApiError = exports.liveBusSocketCandidates = exports.liveBusSocketPath = exports.bucketPromptSize = exports.emitLiveSignal = void 0;
|
|
1414
1726
|
var commandClassifier_1 = require_commandClassifier();
|
|
1415
1727
|
Object.defineProperty(exports, "classifyCommand", { enumerable: true, get: function() {
|
|
1416
1728
|
return commandClassifier_1.classifyCommand;
|
|
@@ -1443,6 +1755,12 @@ var require_out2 = __commonJS({
|
|
|
1443
1755
|
Object.defineProperty(exports, "isAfterHours", { enumerable: true, get: function() {
|
|
1444
1756
|
return afterHours_1.isAfterHours;
|
|
1445
1757
|
} });
|
|
1758
|
+
Object.defineProperty(exports, "isOutsideBusinessHours", { enumerable: true, get: function() {
|
|
1759
|
+
return afterHours_1.isOutsideBusinessHours;
|
|
1760
|
+
} });
|
|
1761
|
+
Object.defineProperty(exports, "BUSINESS_DAY", { enumerable: true, get: function() {
|
|
1762
|
+
return afterHours_1.BUSINESS_DAY;
|
|
1763
|
+
} });
|
|
1446
1764
|
Object.defineProperty(exports, "utcOffsetMinutesAt", { enumerable: true, get: function() {
|
|
1447
1765
|
return afterHours_1.utcOffsetMinutesAt;
|
|
1448
1766
|
} });
|
|
@@ -1507,6 +1825,9 @@ var require_out2 = __commonJS({
|
|
|
1507
1825
|
Object.defineProperty(exports, "loadCliAgentConfig", { enumerable: true, get: function() {
|
|
1508
1826
|
return hookAdapter_1.loadCliAgentConfig;
|
|
1509
1827
|
} });
|
|
1828
|
+
Object.defineProperty(exports, "resolveContextHashes", { enumerable: true, get: function() {
|
|
1829
|
+
return hookAdapter_1.resolveContextHashes;
|
|
1830
|
+
} });
|
|
1510
1831
|
var turnState_1 = require_turnState();
|
|
1511
1832
|
Object.defineProperty(exports, "consumeTurnDurationMs", { enumerable: true, get: function() {
|
|
1512
1833
|
return turnState_1.consumeTurnDurationMs;
|
|
@@ -1550,6 +1871,23 @@ var require_out2 = __commonJS({
|
|
|
1550
1871
|
Object.defineProperty(exports, "hashWithMachineSalt", { enumerable: true, get: function() {
|
|
1551
1872
|
return salt_1.hashWithMachineSalt;
|
|
1552
1873
|
} });
|
|
1874
|
+
var workContext_1 = require_workContext();
|
|
1875
|
+
Object.defineProperty(exports, "deriveWorkContext", { enumerable: true, get: function() {
|
|
1876
|
+
return workContext_1.deriveWorkContext;
|
|
1877
|
+
} });
|
|
1878
|
+
var contextRegistry_1 = require_contextRegistry();
|
|
1879
|
+
Object.defineProperty(exports, "recordWorkContext", { enumerable: true, get: function() {
|
|
1880
|
+
return contextRegistry_1.recordWorkContext;
|
|
1881
|
+
} });
|
|
1882
|
+
Object.defineProperty(exports, "recordWorkContextAlias", { enumerable: true, get: function() {
|
|
1883
|
+
return contextRegistry_1.recordWorkContextAlias;
|
|
1884
|
+
} });
|
|
1885
|
+
Object.defineProperty(exports, "readWorkContextRegistry", { enumerable: true, get: function() {
|
|
1886
|
+
return contextRegistry_1.readWorkContextRegistry;
|
|
1887
|
+
} });
|
|
1888
|
+
Object.defineProperty(exports, "workContextRegistryFilePath", { enumerable: true, get: function() {
|
|
1889
|
+
return contextRegistry_1.workContextRegistryFilePath;
|
|
1890
|
+
} });
|
|
1553
1891
|
var liveBus_1 = require_liveBus();
|
|
1554
1892
|
Object.defineProperty(exports, "emitLiveSignal", { enumerable: true, get: function() {
|
|
1555
1893
|
return liveBus_1.emitLiveSignal;
|
|
@@ -1592,8 +1930,8 @@ var require_out2 = __commonJS({
|
|
|
1592
1930
|
});
|
|
1593
1931
|
|
|
1594
1932
|
// src/cli.ts
|
|
1595
|
-
import * as
|
|
1596
|
-
import * as
|
|
1933
|
+
import * as fs9 from "node:fs/promises";
|
|
1934
|
+
import * as path9 from "node:path";
|
|
1597
1935
|
|
|
1598
1936
|
// src/stores.ts
|
|
1599
1937
|
import * as os from "node:os";
|
|
@@ -1905,6 +2243,107 @@ async function snapshotVsCodeWorkspaceStorage(area, sourceRoot, label) {
|
|
|
1905
2243
|
}
|
|
1906
2244
|
return dest;
|
|
1907
2245
|
}
|
|
2246
|
+
var STAGING_KEEP = ["events.jsonl"];
|
|
2247
|
+
async function entrySizeBytes(target) {
|
|
2248
|
+
let st;
|
|
2249
|
+
try {
|
|
2250
|
+
st = await fs3.lstat(target);
|
|
2251
|
+
} catch {
|
|
2252
|
+
return 0;
|
|
2253
|
+
}
|
|
2254
|
+
if (st.isSymbolicLink()) return 0;
|
|
2255
|
+
if (!st.isDirectory()) return st.size;
|
|
2256
|
+
let total = 0;
|
|
2257
|
+
let entries = [];
|
|
2258
|
+
try {
|
|
2259
|
+
entries = await fs3.readdir(target);
|
|
2260
|
+
} catch {
|
|
2261
|
+
return total;
|
|
2262
|
+
}
|
|
2263
|
+
for (const entry of entries) {
|
|
2264
|
+
total += await entrySizeBytes(path3.join(target, entry));
|
|
2265
|
+
}
|
|
2266
|
+
return total;
|
|
2267
|
+
}
|
|
2268
|
+
async function disposeStagingArea(area, keep = STAGING_KEEP) {
|
|
2269
|
+
const result = { freedBytes: 0, kept: [] };
|
|
2270
|
+
let entries = [];
|
|
2271
|
+
try {
|
|
2272
|
+
entries = await fs3.readdir(area.root);
|
|
2273
|
+
} catch {
|
|
2274
|
+
return result;
|
|
2275
|
+
}
|
|
2276
|
+
for (const entry of entries) {
|
|
2277
|
+
if (keep.includes(entry)) {
|
|
2278
|
+
result.kept.push(entry);
|
|
2279
|
+
continue;
|
|
2280
|
+
}
|
|
2281
|
+
const target = path3.join(area.root, entry);
|
|
2282
|
+
const size = await entrySizeBytes(target);
|
|
2283
|
+
try {
|
|
2284
|
+
await fs3.rm(target, { recursive: true, force: true });
|
|
2285
|
+
result.freedBytes += size;
|
|
2286
|
+
} catch {
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
return result;
|
|
2290
|
+
}
|
|
2291
|
+
async function sweepStagingRoot(stagingRoot, exceptRun, keep = STAGING_KEEP) {
|
|
2292
|
+
const result = { runsSwept: 0, freedBytes: 0 };
|
|
2293
|
+
let runs = [];
|
|
2294
|
+
try {
|
|
2295
|
+
runs = (await fs3.readdir(stagingRoot, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2296
|
+
} catch {
|
|
2297
|
+
return result;
|
|
2298
|
+
}
|
|
2299
|
+
for (const run of runs) {
|
|
2300
|
+
if (run === exceptRun) continue;
|
|
2301
|
+
const disposed = await disposeStagingArea({ extractionId: run, root: path3.join(stagingRoot, run) }, keep);
|
|
2302
|
+
if (disposed.freedBytes > 0) {
|
|
2303
|
+
result.runsSwept += 1;
|
|
2304
|
+
result.freedBytes += disposed.freedBytes;
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
return result;
|
|
2308
|
+
}
|
|
2309
|
+
async function freeSpaceBytes(dir) {
|
|
2310
|
+
let probe = path3.resolve(dir);
|
|
2311
|
+
for (; ; ) {
|
|
2312
|
+
try {
|
|
2313
|
+
const st = await fs3.statfs(probe);
|
|
2314
|
+
return Number(st.bsize) * Number(st.bavail);
|
|
2315
|
+
} catch {
|
|
2316
|
+
const parent = path3.dirname(probe);
|
|
2317
|
+
if (parent === probe) return null;
|
|
2318
|
+
probe = parent;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
async function estimateSnapshotBytes(sources) {
|
|
2323
|
+
let total = 0;
|
|
2324
|
+
for (const source of sources) total += await entrySizeBytes(source);
|
|
2325
|
+
return total;
|
|
2326
|
+
}
|
|
2327
|
+
var SPACE_HEADROOM_BYTES = 2 * 1024 * 1024 * 1024;
|
|
2328
|
+
async function checkSpaceForSnapshot(stagingRoot, sources) {
|
|
2329
|
+
const requiredBytes = await estimateSnapshotBytes(sources) + SPACE_HEADROOM_BYTES;
|
|
2330
|
+
const freeBytes = await freeSpaceBytes(stagingRoot);
|
|
2331
|
+
return {
|
|
2332
|
+
requiredBytes,
|
|
2333
|
+
freeBytes,
|
|
2334
|
+
sufficient: freeBytes === null || freeBytes >= requiredBytes
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
function formatBytes(bytes) {
|
|
2338
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
2339
|
+
let value = bytes;
|
|
2340
|
+
let unit = 0;
|
|
2341
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
2342
|
+
value /= 1024;
|
|
2343
|
+
unit += 1;
|
|
2344
|
+
}
|
|
2345
|
+
return `${value >= 10 || unit === 0 ? Math.round(value) : value.toFixed(1)} ${units[unit]}`;
|
|
2346
|
+
}
|
|
1908
2347
|
|
|
1909
2348
|
// src/extractors/claudeCode.ts
|
|
1910
2349
|
var import_tool_kit = __toESM(require_out2(), 1);
|
|
@@ -2122,7 +2561,7 @@ var ASSUMED_CONTEXT_WINDOW_TOKENS = 2e5;
|
|
|
2122
2561
|
function contextWindowPeakPctOf(fold) {
|
|
2123
2562
|
if (fold.contextWindowPeakTokens <= 0) return 0;
|
|
2124
2563
|
const pct = fold.contextWindowPeakTokens / ASSUMED_CONTEXT_WINDOW_TOKENS;
|
|
2125
|
-
return Math.round(
|
|
2564
|
+
return Math.round(pct * 1e3) / 1e3;
|
|
2126
2565
|
}
|
|
2127
2566
|
async function foldLinesInto(fold, filePath, opts) {
|
|
2128
2567
|
const handle = await fs4.open(filePath);
|
|
@@ -2169,7 +2608,7 @@ async function foldLinesInto(fold, filePath, opts) {
|
|
|
2169
2608
|
fold.subagentPrompts += 1;
|
|
2170
2609
|
} else {
|
|
2171
2610
|
fold.humanPrompts += 1;
|
|
2172
|
-
if (sniffed.occurredAt && (0, import_tool_kit.
|
|
2611
|
+
if (sniffed.occurredAt && (0, import_tool_kit.isOutsideBusinessHours)(new Date(sniffed.occurredAt))) {
|
|
2173
2612
|
fold.afterHoursPrompts += 1;
|
|
2174
2613
|
}
|
|
2175
2614
|
fold.humanPromptTimestamps.push(sniffed.occurredAt);
|
|
@@ -2915,6 +3354,10 @@ import { fileURLToPath } from "node:url";
|
|
|
2915
3354
|
var KNOWN_HISTORY_VERSION = 1;
|
|
2916
3355
|
var KNOWN_CHAT_SESSION_VERSION = 3;
|
|
2917
3356
|
var CHAT_EDIT_PREFIX = "Chat Edit:";
|
|
3357
|
+
function isAbsence(error) {
|
|
3358
|
+
const code = error?.code;
|
|
3359
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
3360
|
+
}
|
|
2918
3361
|
function uriToPath(uri) {
|
|
2919
3362
|
if (typeof uri !== "string" || !uri.startsWith("file://")) return null;
|
|
2920
3363
|
try {
|
|
@@ -2976,6 +3419,7 @@ async function foldEditDays(historyDir, index) {
|
|
|
2976
3419
|
const result = {
|
|
2977
3420
|
days: /* @__PURE__ */ new Map(),
|
|
2978
3421
|
unparsedFiles: 0,
|
|
3422
|
+
unreadableFiles: 0,
|
|
2979
3423
|
malformedEntries: 0,
|
|
2980
3424
|
oldest: null,
|
|
2981
3425
|
newest: null
|
|
@@ -2990,7 +3434,8 @@ async function foldEditDays(historyDir, index) {
|
|
|
2990
3434
|
let raw;
|
|
2991
3435
|
try {
|
|
2992
3436
|
raw = await fs6.readFile(path6.join(historyDir, hash, "entries.json"), "utf8");
|
|
2993
|
-
} catch {
|
|
3437
|
+
} catch (error) {
|
|
3438
|
+
if (!isAbsence(error)) result.unreadableFiles += 1;
|
|
2994
3439
|
continue;
|
|
2995
3440
|
}
|
|
2996
3441
|
const sniffed = sniffHistoryFile(raw);
|
|
@@ -3032,9 +3477,9 @@ async function foldEditDays(historyDir, index) {
|
|
|
3032
3477
|
var JSONL_KIND_HEADER = 0;
|
|
3033
3478
|
var JSONL_KIND_SET = 1;
|
|
3034
3479
|
var JSONL_KIND_SPLICE = 2;
|
|
3035
|
-
function resolvePath(root,
|
|
3480
|
+
function resolvePath(root, path10) {
|
|
3036
3481
|
let cur = root;
|
|
3037
|
-
for (const seg of
|
|
3482
|
+
for (const seg of path10) {
|
|
3038
3483
|
if (typeof seg === "number") {
|
|
3039
3484
|
if (!Array.isArray(cur) || seg >= cur.length) return null;
|
|
3040
3485
|
cur = cur[seg];
|
|
@@ -3045,10 +3490,10 @@ function resolvePath(root, path9) {
|
|
|
3045
3490
|
}
|
|
3046
3491
|
return cur ?? null;
|
|
3047
3492
|
}
|
|
3048
|
-
function applySet(root,
|
|
3049
|
-
if (
|
|
3050
|
-
const parent = resolvePath(root,
|
|
3051
|
-
const last =
|
|
3493
|
+
function applySet(root, path10, value) {
|
|
3494
|
+
if (path10.length === 0) return;
|
|
3495
|
+
const parent = resolvePath(root, path10.slice(0, -1));
|
|
3496
|
+
const last = path10[path10.length - 1];
|
|
3052
3497
|
if (typeof last === "number") {
|
|
3053
3498
|
if (!Array.isArray(parent)) return;
|
|
3054
3499
|
while (parent.length <= last) parent.push(null);
|
|
@@ -3058,8 +3503,8 @@ function applySet(root, path9, value) {
|
|
|
3058
3503
|
parent[last] = value;
|
|
3059
3504
|
}
|
|
3060
3505
|
}
|
|
3061
|
-
function applySplice(root,
|
|
3062
|
-
const target = resolvePath(root,
|
|
3506
|
+
function applySplice(root, path10, values, index) {
|
|
3507
|
+
const target = resolvePath(root, path10);
|
|
3063
3508
|
if (!Array.isArray(target)) return;
|
|
3064
3509
|
if (index !== null && index >= 0 && index <= target.length) target.splice(index, 0, ...values);
|
|
3065
3510
|
else target.push(...values);
|
|
@@ -3116,6 +3561,7 @@ async function foldChatSessions(workspaceStorageDir) {
|
|
|
3116
3561
|
const result = {
|
|
3117
3562
|
sessions: [],
|
|
3118
3563
|
unparsedFiles: 0,
|
|
3564
|
+
unreadableFiles: 0,
|
|
3119
3565
|
unrecognisedFiles: 0,
|
|
3120
3566
|
emptyDrafts: 0,
|
|
3121
3567
|
malformedLines: 0
|
|
@@ -3148,6 +3594,7 @@ async function foldChatSessions(workspaceStorageDir) {
|
|
|
3148
3594
|
try {
|
|
3149
3595
|
raw = await fs6.readFile(path6.join(wsDir, "chatSessions", file), "utf8");
|
|
3150
3596
|
} catch {
|
|
3597
|
+
result.unreadableFiles += 1;
|
|
3151
3598
|
continue;
|
|
3152
3599
|
}
|
|
3153
3600
|
let record = null;
|
|
@@ -3213,9 +3660,9 @@ async function foldChatSessions(workspaceStorageDir) {
|
|
|
3213
3660
|
}
|
|
3214
3661
|
return result;
|
|
3215
3662
|
}
|
|
3216
|
-
async function* extractVsCode(
|
|
3217
|
-
const historyDir = path6.join(
|
|
3218
|
-
const workspaceStorageDir = path6.join(
|
|
3663
|
+
async function* extractVsCode(source, extractionId) {
|
|
3664
|
+
const historyDir = typeof source === "string" ? path6.join(source, "history") : source.historyDir;
|
|
3665
|
+
const workspaceStorageDir = typeof source === "string" ? path6.join(source, "workspaceStorage") : source.workspaceStorageDir;
|
|
3219
3666
|
const index = await buildWorkspaceFolderIndex(workspaceStorageDir);
|
|
3220
3667
|
const editDays = await foldEditDays(historyDir, index);
|
|
3221
3668
|
const chatSessions = await foldChatSessions(workspaceStorageDir);
|
|
@@ -3315,7 +3762,7 @@ async function* extractVsCode(snapshotDir, extractionId) {
|
|
|
3315
3762
|
};
|
|
3316
3763
|
}
|
|
3317
3764
|
}
|
|
3318
|
-
const readFailures = editDays.unparsedFiles + editDays.malformedEntries + chatSessions.unparsedFiles + chatSessions.unrecognisedFiles + chatSessions.malformedLines;
|
|
3765
|
+
const readFailures = editDays.unparsedFiles + editDays.unreadableFiles + editDays.malformedEntries + chatSessions.unparsedFiles + chatSessions.unreadableFiles + chatSessions.unrecognisedFiles + chatSessions.malformedLines;
|
|
3319
3766
|
if (windowOldest && windowNewest || readFailures > 0) {
|
|
3320
3767
|
const window = windowOldest && windowNewest ? { windowOldest, windowNewest } : {};
|
|
3321
3768
|
yield {
|
|
@@ -3332,8 +3779,10 @@ async function* extractVsCode(snapshotDir, extractionId) {
|
|
|
3332
3779
|
editDayCount: editDays.days.size,
|
|
3333
3780
|
sessionCount,
|
|
3334
3781
|
unparsedHistoryFiles: editDays.unparsedFiles,
|
|
3782
|
+
unreadableHistoryFiles: editDays.unreadableFiles,
|
|
3335
3783
|
malformedHistoryEntries: editDays.malformedEntries,
|
|
3336
3784
|
unparsedChatSessionFiles: chatSessions.unparsedFiles,
|
|
3785
|
+
unreadableChatSessionFiles: chatSessions.unreadableFiles,
|
|
3337
3786
|
unrecognisedChatSessionFiles: chatSessions.unrecognisedFiles,
|
|
3338
3787
|
emptyChatSessions: chatSessions.emptyDrafts,
|
|
3339
3788
|
malformedChatSessionLines: chatSessions.malformedLines
|
|
@@ -3344,9 +3793,314 @@ async function* extractVsCode(snapshotDir, extractionId) {
|
|
|
3344
3793
|
}
|
|
3345
3794
|
}
|
|
3346
3795
|
|
|
3796
|
+
// src/archive.ts
|
|
3797
|
+
import { createHash } from "node:crypto";
|
|
3798
|
+
import { createReadStream } from "node:fs";
|
|
3799
|
+
import * as fs7 from "node:fs/promises";
|
|
3800
|
+
import * as path7 from "node:path";
|
|
3801
|
+
import { pipeline } from "node:stream/promises";
|
|
3802
|
+
var ARCHIVE_SCHEMA = 1;
|
|
3803
|
+
function defaultArchiveRoot(home) {
|
|
3804
|
+
return path7.join(home, ".ascenda", "history-import", "archive");
|
|
3805
|
+
}
|
|
3806
|
+
function defaultArchiveSources(paths, includeVsCodeSessions = false) {
|
|
3807
|
+
const sources = [
|
|
3808
|
+
{ store: "claude_code", root: paths.claudeProjects, label: "projects" },
|
|
3809
|
+
{ store: "cursor", root: paths.cursorStateDb, siblings: ["-wal", "-shm"], label: "state" },
|
|
3810
|
+
{ store: "vscode", root: paths.vscodeHistory, label: "history" }
|
|
3811
|
+
];
|
|
3812
|
+
if (includeVsCodeSessions) {
|
|
3813
|
+
sources.push({ store: "vscode", root: paths.vscodeWorkspaceStorage, label: "workspaceStorage" });
|
|
3814
|
+
}
|
|
3815
|
+
return sources;
|
|
3816
|
+
}
|
|
3817
|
+
function blobPath(archiveRoot, sha256) {
|
|
3818
|
+
return path7.join(archiveRoot, "objects", sha256.slice(0, 2), sha256);
|
|
3819
|
+
}
|
|
3820
|
+
function manifestDir(archiveRoot) {
|
|
3821
|
+
return path7.join(archiveRoot, "manifests");
|
|
3822
|
+
}
|
|
3823
|
+
async function hashFile(file) {
|
|
3824
|
+
const hash = createHash("sha256");
|
|
3825
|
+
await pipeline(createReadStream(file), hash);
|
|
3826
|
+
return hash.digest("hex");
|
|
3827
|
+
}
|
|
3828
|
+
async function walkFiles(root) {
|
|
3829
|
+
let st;
|
|
3830
|
+
try {
|
|
3831
|
+
st = await fs7.stat(root);
|
|
3832
|
+
} catch {
|
|
3833
|
+
return [];
|
|
3834
|
+
}
|
|
3835
|
+
if (!st.isDirectory()) return [{ absolute: root, relative: path7.basename(root) }];
|
|
3836
|
+
const found = [];
|
|
3837
|
+
async function walk(dir, prefix) {
|
|
3838
|
+
let entries;
|
|
3839
|
+
try {
|
|
3840
|
+
entries = await fs7.readdir(dir, { withFileTypes: true });
|
|
3841
|
+
} catch {
|
|
3842
|
+
return;
|
|
3843
|
+
}
|
|
3844
|
+
for (const entry of entries) {
|
|
3845
|
+
const absolute = path7.join(dir, entry.name);
|
|
3846
|
+
const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
3847
|
+
if (entry.isDirectory()) await walk(absolute, relative);
|
|
3848
|
+
else if (entry.isFile()) found.push({ absolute, relative });
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
await walk(root, "");
|
|
3852
|
+
return found;
|
|
3853
|
+
}
|
|
3854
|
+
async function listManifests(archiveRoot) {
|
|
3855
|
+
try {
|
|
3856
|
+
return (await fs7.readdir(manifestDir(archiveRoot))).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -".json".length)).sort();
|
|
3857
|
+
} catch {
|
|
3858
|
+
return [];
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
async function readManifest(archiveRoot, generation) {
|
|
3862
|
+
try {
|
|
3863
|
+
const raw = await fs7.readFile(path7.join(manifestDir(archiveRoot), `${generation}.json`), "utf8");
|
|
3864
|
+
const parsed = JSON.parse(raw);
|
|
3865
|
+
if (parsed.schema !== ARCHIVE_SCHEMA) return null;
|
|
3866
|
+
return parsed;
|
|
3867
|
+
} catch {
|
|
3868
|
+
return null;
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
async function readLatestManifest(archiveRoot) {
|
|
3872
|
+
const generations = await listManifests(archiveRoot);
|
|
3873
|
+
const latest = generations[generations.length - 1];
|
|
3874
|
+
return latest ? readManifest(archiveRoot, latest) : null;
|
|
3875
|
+
}
|
|
3876
|
+
async function archiveStores(options) {
|
|
3877
|
+
const { archiveRoot, sources, generation, now } = options;
|
|
3878
|
+
const result = {
|
|
3879
|
+
generation,
|
|
3880
|
+
manifestPath: path7.join(manifestDir(archiveRoot), `${generation}.json`),
|
|
3881
|
+
filesArchived: 0,
|
|
3882
|
+
filesDeduplicated: 0,
|
|
3883
|
+
newBytes: 0,
|
|
3884
|
+
totalBytes: 0,
|
|
3885
|
+
perStore: {},
|
|
3886
|
+
unreadable: 0
|
|
3887
|
+
};
|
|
3888
|
+
await fs7.mkdir(path7.join(archiveRoot, "objects"), { recursive: true });
|
|
3889
|
+
await fs7.mkdir(manifestDir(archiveRoot), { recursive: true });
|
|
3890
|
+
const previous = await readLatestManifest(archiveRoot);
|
|
3891
|
+
const previousByKey = /* @__PURE__ */ new Map();
|
|
3892
|
+
for (const file of previous?.files ?? []) {
|
|
3893
|
+
previousByKey.set(`${file.store}/${file.relativePath}`, file);
|
|
3894
|
+
}
|
|
3895
|
+
const files = [];
|
|
3896
|
+
const roots = {};
|
|
3897
|
+
for (const source of sources) {
|
|
3898
|
+
roots[`${source.store}/${source.label}`] = source.root;
|
|
3899
|
+
const targets = await walkFiles(source.root);
|
|
3900
|
+
for (const suffix of source.siblings ?? []) {
|
|
3901
|
+
const absolute = source.root + suffix;
|
|
3902
|
+
try {
|
|
3903
|
+
await fs7.stat(absolute);
|
|
3904
|
+
targets.push({ absolute, relative: path7.basename(absolute) });
|
|
3905
|
+
} catch {
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
const storeStats = result.perStore[source.store] ??= {
|
|
3909
|
+
files: 0,
|
|
3910
|
+
newBytes: 0
|
|
3911
|
+
};
|
|
3912
|
+
for (const target of targets) {
|
|
3913
|
+
const relativePath = `${source.label}/${target.relative}`;
|
|
3914
|
+
let st;
|
|
3915
|
+
try {
|
|
3916
|
+
st = await fs7.stat(target.absolute);
|
|
3917
|
+
} catch {
|
|
3918
|
+
result.unreadable += 1;
|
|
3919
|
+
continue;
|
|
3920
|
+
}
|
|
3921
|
+
const prior = previousByKey.get(`${source.store}/${relativePath}`);
|
|
3922
|
+
let sha256 = null;
|
|
3923
|
+
if (prior && prior.size === st.size && prior.mtimeMs === st.mtimeMs) {
|
|
3924
|
+
try {
|
|
3925
|
+
await fs7.stat(blobPath(archiveRoot, prior.sha256));
|
|
3926
|
+
sha256 = prior.sha256;
|
|
3927
|
+
} catch {
|
|
3928
|
+
sha256 = null;
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
if (sha256 === null) {
|
|
3932
|
+
try {
|
|
3933
|
+
sha256 = await hashFile(target.absolute);
|
|
3934
|
+
} catch {
|
|
3935
|
+
result.unreadable += 1;
|
|
3936
|
+
continue;
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
const destination = blobPath(archiveRoot, sha256);
|
|
3940
|
+
let alreadyHeld = true;
|
|
3941
|
+
try {
|
|
3942
|
+
await fs7.stat(destination);
|
|
3943
|
+
} catch {
|
|
3944
|
+
alreadyHeld = false;
|
|
3945
|
+
}
|
|
3946
|
+
if (alreadyHeld) {
|
|
3947
|
+
result.filesDeduplicated += 1;
|
|
3948
|
+
} else {
|
|
3949
|
+
await fs7.mkdir(path7.dirname(destination), { recursive: true });
|
|
3950
|
+
const temporary2 = `${destination}.tmp-${generation}`;
|
|
3951
|
+
try {
|
|
3952
|
+
await fs7.copyFile(target.absolute, temporary2);
|
|
3953
|
+
await fs7.rename(temporary2, destination);
|
|
3954
|
+
result.newBytes += st.size;
|
|
3955
|
+
storeStats.newBytes += st.size;
|
|
3956
|
+
} catch {
|
|
3957
|
+
await fs7.rm(temporary2, { force: true });
|
|
3958
|
+
result.unreadable += 1;
|
|
3959
|
+
continue;
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
files.push({
|
|
3963
|
+
store: source.store,
|
|
3964
|
+
relativePath,
|
|
3965
|
+
sha256,
|
|
3966
|
+
size: st.size,
|
|
3967
|
+
mtimeMs: st.mtimeMs
|
|
3968
|
+
});
|
|
3969
|
+
result.filesArchived += 1;
|
|
3970
|
+
result.totalBytes += st.size;
|
|
3971
|
+
storeStats.files += 1;
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
const manifest = {
|
|
3975
|
+
schema: ARCHIVE_SCHEMA,
|
|
3976
|
+
generation,
|
|
3977
|
+
createdAt: now,
|
|
3978
|
+
roots,
|
|
3979
|
+
files
|
|
3980
|
+
};
|
|
3981
|
+
const temporary = `${result.manifestPath}.tmp`;
|
|
3982
|
+
await fs7.writeFile(temporary, JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
3983
|
+
await fs7.rename(temporary, result.manifestPath);
|
|
3984
|
+
return result;
|
|
3985
|
+
}
|
|
3986
|
+
async function verifyArchive(archiveRoot, manifest, deep = true) {
|
|
3987
|
+
const result = { generation: manifest.generation, checked: 0, missing: [], corrupted: [] };
|
|
3988
|
+
const hashed = /* @__PURE__ */ new Set();
|
|
3989
|
+
for (const file of manifest.files) {
|
|
3990
|
+
const blob = blobPath(archiveRoot, file.sha256);
|
|
3991
|
+
result.checked += 1;
|
|
3992
|
+
try {
|
|
3993
|
+
await fs7.stat(blob);
|
|
3994
|
+
} catch {
|
|
3995
|
+
result.missing.push(file.relativePath);
|
|
3996
|
+
continue;
|
|
3997
|
+
}
|
|
3998
|
+
if (!deep || hashed.has(file.sha256)) continue;
|
|
3999
|
+
hashed.add(file.sha256);
|
|
4000
|
+
let actual;
|
|
4001
|
+
try {
|
|
4002
|
+
actual = await hashFile(blob);
|
|
4003
|
+
} catch {
|
|
4004
|
+
result.corrupted.push(file.relativePath);
|
|
4005
|
+
continue;
|
|
4006
|
+
}
|
|
4007
|
+
if (actual !== file.sha256) result.corrupted.push(file.relativePath);
|
|
4008
|
+
}
|
|
4009
|
+
return result;
|
|
4010
|
+
}
|
|
4011
|
+
async function restoreArchive(archiveRoot, manifest, destination) {
|
|
4012
|
+
const result = { restored: 0, skipped: 0, destination };
|
|
4013
|
+
for (const file of manifest.files) {
|
|
4014
|
+
const blob = blobPath(archiveRoot, file.sha256);
|
|
4015
|
+
const target = path7.join(destination, file.store, file.relativePath);
|
|
4016
|
+
try {
|
|
4017
|
+
await fs7.mkdir(path7.dirname(target), { recursive: true });
|
|
4018
|
+
await fs7.copyFile(blob, target);
|
|
4019
|
+
result.restored += 1;
|
|
4020
|
+
} catch {
|
|
4021
|
+
result.skipped += 1;
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
return result;
|
|
4025
|
+
}
|
|
4026
|
+
async function pruneArchive(archiveRoot, keep) {
|
|
4027
|
+
const result = { generationsRemoved: [], blobsRemoved: 0, freedBytes: 0 };
|
|
4028
|
+
const generations = await listManifests(archiveRoot);
|
|
4029
|
+
const doomed = keep >= generations.length ? [] : generations.slice(0, generations.length - keep);
|
|
4030
|
+
for (const generation of doomed) {
|
|
4031
|
+
try {
|
|
4032
|
+
await fs7.rm(path7.join(manifestDir(archiveRoot), `${generation}.json`), { force: true });
|
|
4033
|
+
result.generationsRemoved.push(generation);
|
|
4034
|
+
} catch {
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
const live = /* @__PURE__ */ new Set();
|
|
4038
|
+
for (const generation of await listManifests(archiveRoot)) {
|
|
4039
|
+
const manifest = await readManifest(archiveRoot, generation);
|
|
4040
|
+
for (const file of manifest?.files ?? []) live.add(file.sha256);
|
|
4041
|
+
}
|
|
4042
|
+
const objectsRoot = path7.join(archiveRoot, "objects");
|
|
4043
|
+
let shards = [];
|
|
4044
|
+
try {
|
|
4045
|
+
shards = (await fs7.readdir(objectsRoot, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
4046
|
+
} catch {
|
|
4047
|
+
return result;
|
|
4048
|
+
}
|
|
4049
|
+
for (const shard of shards) {
|
|
4050
|
+
let blobs = [];
|
|
4051
|
+
try {
|
|
4052
|
+
blobs = await fs7.readdir(path7.join(objectsRoot, shard));
|
|
4053
|
+
} catch {
|
|
4054
|
+
continue;
|
|
4055
|
+
}
|
|
4056
|
+
for (const blob of blobs) {
|
|
4057
|
+
if (live.has(blob)) continue;
|
|
4058
|
+
const target = path7.join(objectsRoot, shard, blob);
|
|
4059
|
+
let size = 0;
|
|
4060
|
+
try {
|
|
4061
|
+
size = (await fs7.stat(target)).size;
|
|
4062
|
+
} catch {
|
|
4063
|
+
continue;
|
|
4064
|
+
}
|
|
4065
|
+
try {
|
|
4066
|
+
await fs7.rm(target, { force: true });
|
|
4067
|
+
result.blobsRemoved += 1;
|
|
4068
|
+
result.freedBytes += size;
|
|
4069
|
+
} catch {
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
return result;
|
|
4074
|
+
}
|
|
4075
|
+
async function archiveSizeBytes(archiveRoot) {
|
|
4076
|
+
const objectsRoot = path7.join(archiveRoot, "objects");
|
|
4077
|
+
let total = 0;
|
|
4078
|
+
let shards = [];
|
|
4079
|
+
try {
|
|
4080
|
+
shards = (await fs7.readdir(objectsRoot, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
4081
|
+
} catch {
|
|
4082
|
+
return 0;
|
|
4083
|
+
}
|
|
4084
|
+
for (const shard of shards) {
|
|
4085
|
+
let blobs = [];
|
|
4086
|
+
try {
|
|
4087
|
+
blobs = await fs7.readdir(path7.join(objectsRoot, shard));
|
|
4088
|
+
} catch {
|
|
4089
|
+
continue;
|
|
4090
|
+
}
|
|
4091
|
+
for (const blob of blobs) {
|
|
4092
|
+
try {
|
|
4093
|
+
total += (await fs7.stat(path7.join(objectsRoot, shard, blob))).size;
|
|
4094
|
+
} catch {
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
return total;
|
|
4099
|
+
}
|
|
4100
|
+
|
|
3347
4101
|
// src/ship.ts
|
|
3348
4102
|
var import_tool_kit4 = __toESM(require_out2(), 1);
|
|
3349
|
-
import { createHash } from "node:crypto";
|
|
4103
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3350
4104
|
var DEFAULT_API_BASE_URL = "https://api.ascenda.one";
|
|
3351
4105
|
function loadShipConfig() {
|
|
3352
4106
|
const apiBaseUrl = (process.env.ASCENDA_API_BASE_URL ?? DEFAULT_API_BASE_URL).replace(/\/$/, "");
|
|
@@ -3376,12 +4130,28 @@ function importOrdinals(events) {
|
|
|
3376
4130
|
});
|
|
3377
4131
|
}
|
|
3378
4132
|
function importKeyOf(event, ordinal) {
|
|
3379
|
-
return
|
|
4133
|
+
return createHash2("sha256").update(
|
|
3380
4134
|
[event.store, event.sessionRef ?? "", event.eventKind, event.occurredAt, String(ordinal)].join(
|
|
3381
4135
|
"|"
|
|
3382
4136
|
)
|
|
3383
4137
|
).digest("hex").slice(0, 16);
|
|
3384
4138
|
}
|
|
4139
|
+
var workContextMemo = /* @__PURE__ */ new Map();
|
|
4140
|
+
function workContextOf(repoRef) {
|
|
4141
|
+
if (!repoRef) return null;
|
|
4142
|
+
const hit = workContextMemo.get(repoRef);
|
|
4143
|
+
if (hit !== void 0) return hit;
|
|
4144
|
+
const context = (0, import_tool_kit4.deriveWorkContext)(repoRef);
|
|
4145
|
+
if (context) {
|
|
4146
|
+
(0, import_tool_kit4.recordWorkContext)(context);
|
|
4147
|
+
const legacyHash = (0, import_tool_kit4.hashWithMachineSalt)(repoRef);
|
|
4148
|
+
if (legacyHash && legacyHash !== context.workspaceHash && legacyHash !== context.projectHash) {
|
|
4149
|
+
(0, import_tool_kit4.recordWorkContextAlias)(legacyHash, context.workspaceLabel ?? repoRef, context.workspacePath);
|
|
4150
|
+
}
|
|
4151
|
+
}
|
|
4152
|
+
workContextMemo.set(repoRef, context);
|
|
4153
|
+
return context;
|
|
4154
|
+
}
|
|
3385
4155
|
function toWirePayload(event, ordinal, toolInstallationId) {
|
|
3386
4156
|
const metadata = {
|
|
3387
4157
|
importKey: importKeyOf(event, ordinal),
|
|
@@ -3396,7 +4166,7 @@ function toWirePayload(event, ordinal, toolInstallationId) {
|
|
|
3396
4166
|
metadata.gitBranchHash = (0, import_tool_kit4.hashWithMachineSalt)(metadata.gitBranch) ?? "";
|
|
3397
4167
|
delete metadata.gitBranch;
|
|
3398
4168
|
}
|
|
3399
|
-
const
|
|
4169
|
+
const context = workContextOf(event.repoRef);
|
|
3400
4170
|
return {
|
|
3401
4171
|
toolInstallationId,
|
|
3402
4172
|
source: STORE_SOURCE[event.store],
|
|
@@ -3412,8 +4182,8 @@ function toWirePayload(event, ordinal, toolInstallationId) {
|
|
|
3412
4182
|
utcOffsetMinutes: (0, import_tool_kit4.utcOffsetMinutesAt)(new Date(event.occurredAt)),
|
|
3413
4183
|
severity: "low",
|
|
3414
4184
|
sessionId: event.sessionRef,
|
|
3415
|
-
workspaceHash,
|
|
3416
|
-
projectHash:
|
|
4185
|
+
workspaceHash: context?.workspaceHash ?? null,
|
|
4186
|
+
projectHash: context?.projectHash ?? null,
|
|
3417
4187
|
// A real contract ToolConsentScope now, and a real gate: the backend
|
|
3418
4188
|
// requires an active historical-import consent lease for anything carrying
|
|
3419
4189
|
// one of the provenance classes below, and decides that on the provenance
|
|
@@ -3425,6 +4195,9 @@ function toWirePayload(event, ordinal, toolInstallationId) {
|
|
|
3425
4195
|
metadata
|
|
3426
4196
|
};
|
|
3427
4197
|
}
|
|
4198
|
+
function storeCounts(result, store) {
|
|
4199
|
+
return result.perStore[store] ??= { sent: 0, accepted: 0, duplicate: 0, rejected: 0 };
|
|
4200
|
+
}
|
|
3428
4201
|
var BATCH_SIZE = 200;
|
|
3429
4202
|
function shippableEvents(events) {
|
|
3430
4203
|
return events.filter(
|
|
@@ -3438,7 +4211,9 @@ async function shipEvents(events, config, onProgress) {
|
|
|
3438
4211
|
duplicate: 0,
|
|
3439
4212
|
rejected: 0,
|
|
3440
4213
|
rejectionReasons: {},
|
|
3441
|
-
httpFailures: 0
|
|
4214
|
+
httpFailures: 0,
|
|
4215
|
+
perStore: {},
|
|
4216
|
+
attributionComplete: true
|
|
3442
4217
|
};
|
|
3443
4218
|
const wireEvents = shippableEvents(events);
|
|
3444
4219
|
const ordinals = importOrdinals(wireEvents);
|
|
@@ -3459,11 +4234,14 @@ async function shipEvents(events, config, onProgress) {
|
|
|
3459
4234
|
});
|
|
3460
4235
|
} catch {
|
|
3461
4236
|
result.httpFailures += 1;
|
|
4237
|
+
result.attributionComplete = false;
|
|
3462
4238
|
continue;
|
|
3463
4239
|
}
|
|
3464
4240
|
result.sent += chunk.length;
|
|
4241
|
+
for (const event of chunk) storeCounts(result, event.store).sent += 1;
|
|
3465
4242
|
if (!response.ok) {
|
|
3466
4243
|
result.httpFailures += 1;
|
|
4244
|
+
result.attributionComplete = false;
|
|
3467
4245
|
if (response.status === 401 || response.status === 403) {
|
|
3468
4246
|
throw new Error(
|
|
3469
4247
|
`Ingest refused (${response.status}) \u2014 token invalid/revoked or consent missing; aborting rather than burning ${wireEvents.length - offset} more events`
|
|
@@ -3476,14 +4254,38 @@ async function shipEvents(events, config, onProgress) {
|
|
|
3476
4254
|
result.accepted += body.accepted ?? 0;
|
|
3477
4255
|
result.duplicate += body.duplicate ?? 0;
|
|
3478
4256
|
result.rejected += body.rejected ?? 0;
|
|
3479
|
-
|
|
4257
|
+
const items = body.results ?? [];
|
|
4258
|
+
const attributable = items.length === chunk.length;
|
|
4259
|
+
if (!attributable && (body.accepted ?? body.duplicate ?? body.rejected) !== void 0) {
|
|
4260
|
+
result.attributionComplete = false;
|
|
4261
|
+
}
|
|
4262
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
4263
|
+
const item = items[i];
|
|
4264
|
+
if (attributable) {
|
|
4265
|
+
const counts = storeCounts(result, chunk[i].store);
|
|
4266
|
+
if (item.status === "accepted") counts.accepted += 1;
|
|
4267
|
+
else if (item.status === "duplicate") counts.duplicate += 1;
|
|
4268
|
+
else counts.rejected += 1;
|
|
4269
|
+
}
|
|
3480
4270
|
if (item.status !== "accepted" && item.status !== "duplicate") {
|
|
3481
4271
|
const reason = item.reason ?? "unknown";
|
|
3482
4272
|
result.rejectionReasons[reason] = (result.rejectionReasons[reason] ?? 0) + 1;
|
|
3483
4273
|
}
|
|
3484
4274
|
}
|
|
4275
|
+
if (items.length === chunk.length && items.length > 0) {
|
|
4276
|
+
const blocked = items.every(
|
|
4277
|
+
(item) => item.status !== "accepted" && item.status !== "duplicate" && (item.reason === "consent_missing_or_expired" || item.reason === "unknown_consent_scope")
|
|
4278
|
+
);
|
|
4279
|
+
if (blocked) {
|
|
4280
|
+
result.consentBlocked = true;
|
|
4281
|
+
result.attributionComplete = false;
|
|
4282
|
+
onProgress?.(Math.min(offset + BATCH_SIZE, wireEvents.length), wireEvents.length);
|
|
4283
|
+
return result;
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
3485
4286
|
} catch {
|
|
3486
4287
|
result.httpFailures += 1;
|
|
4288
|
+
result.attributionComplete = false;
|
|
3487
4289
|
}
|
|
3488
4290
|
onProgress?.(Math.min(offset + BATCH_SIZE, wireEvents.length), wireEvents.length);
|
|
3489
4291
|
}
|
|
@@ -3491,16 +4293,16 @@ async function shipEvents(events, config, onProgress) {
|
|
|
3491
4293
|
}
|
|
3492
4294
|
|
|
3493
4295
|
// src/localHandoff.ts
|
|
3494
|
-
import * as
|
|
4296
|
+
import * as fs8 from "node:fs/promises";
|
|
3495
4297
|
import * as os2 from "node:os";
|
|
3496
|
-
import * as
|
|
4298
|
+
import * as path8 from "node:path";
|
|
3497
4299
|
var APP_BUNDLE_ID = "one.ascenda.ascendaMissionControl";
|
|
3498
4300
|
var HANDOFF_SCHEMA = 2;
|
|
3499
4301
|
function handoffDir(home = os2.homedir()) {
|
|
3500
|
-
return
|
|
4302
|
+
return path8.join(home, "Library", "Containers", APP_BUNDLE_ID, "Data", ".ascenda", "history-import");
|
|
3501
4303
|
}
|
|
3502
4304
|
function handoffFilePath(home = os2.homedir(), store = "claude_code") {
|
|
3503
|
-
return
|
|
4305
|
+
return path8.join(handoffDir(home), `${store}.json`);
|
|
3504
4306
|
}
|
|
3505
4307
|
function projectLabelOf(repoRef) {
|
|
3506
4308
|
if (!repoRef) return null;
|
|
@@ -3534,7 +4336,15 @@ function buildHandoff(events, extractionId, generatedAt) {
|
|
|
3534
4336
|
compactionCount: Number(event.metrics.compactionCount ?? 0),
|
|
3535
4337
|
toolFailureCount: Number(event.metrics.toolFailureCount ?? 0),
|
|
3536
4338
|
contextWindowPeakPct: Number(event.metrics.contextWindowPeakPct ?? 0),
|
|
3537
|
-
|
|
4339
|
+
contextWindowPeakTokens: Number(event.metrics.contextWindowPeakTokens ?? 0),
|
|
4340
|
+
// Null, never 0. `toolUseResult.userModified` occurs 20,133 times
|
|
4341
|
+
// across a real 363-session store and is `false` on every single one
|
|
4342
|
+
// — Claude Code does not appear to set it true. Shipping 0 says "no
|
|
4343
|
+
// AI edit was ever corrected by hand", which is a finding; the truth
|
|
4344
|
+
// is that this store cannot answer the question. The extractor keeps
|
|
4345
|
+
// counting it in case upstream starts populating it, and the handoff
|
|
4346
|
+
// reports the count only when it is non-zero.
|
|
4347
|
+
userModifiedEditCount: Number(event.metrics.userModifiedEditCount ?? 0) > 0 ? Number(event.metrics.userModifiedEditCount) : null,
|
|
3538
4348
|
subagentTranscripts: Number(event.metrics.subagentTranscripts ?? 0),
|
|
3539
4349
|
days: event.dayBreakdown ?? [],
|
|
3540
4350
|
provenance: event.provenance
|
|
@@ -3651,18 +4461,18 @@ function buildVsCodeHandoff(events, extractionId, generatedAt) {
|
|
|
3651
4461
|
};
|
|
3652
4462
|
}
|
|
3653
4463
|
async function writeHandoff(handoff, home = os2.homedir()) {
|
|
3654
|
-
const containerRoot =
|
|
4464
|
+
const containerRoot = path8.join(home, "Library", "Containers", APP_BUNDLE_ID, "Data");
|
|
3655
4465
|
try {
|
|
3656
|
-
await
|
|
4466
|
+
await fs8.stat(containerRoot);
|
|
3657
4467
|
} catch {
|
|
3658
4468
|
return null;
|
|
3659
4469
|
}
|
|
3660
4470
|
const dir = handoffDir(home);
|
|
3661
|
-
await
|
|
4471
|
+
await fs8.mkdir(dir, { recursive: true });
|
|
3662
4472
|
const target = handoffFilePath(home, handoff.store);
|
|
3663
4473
|
const temp = `${target}.tmp`;
|
|
3664
|
-
await
|
|
3665
|
-
await
|
|
4474
|
+
await fs8.writeFile(temp, JSON.stringify(handoff, null, 2) + "\n", "utf8");
|
|
4475
|
+
await fs8.rename(temp, target);
|
|
3666
4476
|
return target;
|
|
3667
4477
|
}
|
|
3668
4478
|
|
|
@@ -3682,6 +4492,185 @@ function formatInventory(inv) {
|
|
|
3682
4492
|
}
|
|
3683
4493
|
return lines.join("\n");
|
|
3684
4494
|
}
|
|
4495
|
+
async function safeScan(store, rootPath, scan) {
|
|
4496
|
+
try {
|
|
4497
|
+
return { inventory: await scan(), scanError: null };
|
|
4498
|
+
} catch (error) {
|
|
4499
|
+
return {
|
|
4500
|
+
inventory: { store, rootPath, present: false, counts: {}, notes: [] },
|
|
4501
|
+
scanError: error instanceof Error ? error.message : String(error)
|
|
4502
|
+
};
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4505
|
+
var READ_FAILURE_METRICS = [
|
|
4506
|
+
"unparsedFiles",
|
|
4507
|
+
"unreadableFiles",
|
|
4508
|
+
"unparsedHistoryFiles",
|
|
4509
|
+
"unreadableHistoryFiles",
|
|
4510
|
+
"malformedHistoryEntries",
|
|
4511
|
+
"unparsedChatSessionFiles",
|
|
4512
|
+
"unreadableChatSessionFiles",
|
|
4513
|
+
"unrecognisedChatSessionFiles",
|
|
4514
|
+
"malformedChatSessionLines",
|
|
4515
|
+
"unparsedLines",
|
|
4516
|
+
"unparsedTranscripts"
|
|
4517
|
+
];
|
|
4518
|
+
function readFailuresOf(events) {
|
|
4519
|
+
let total = 0;
|
|
4520
|
+
for (const event of events) {
|
|
4521
|
+
if (event.eventKind !== "extraction_epoch") continue;
|
|
4522
|
+
for (const metric of READ_FAILURE_METRICS) {
|
|
4523
|
+
const value = event.metrics[metric];
|
|
4524
|
+
if (typeof value === "number") total += value;
|
|
4525
|
+
}
|
|
4526
|
+
}
|
|
4527
|
+
return total;
|
|
4528
|
+
}
|
|
4529
|
+
async function runSource(outcomes, store, inventory, allEvents, extract, buildStoreHandoff, scanError = null) {
|
|
4530
|
+
if (scanError !== null) {
|
|
4531
|
+
process.stdout.write(`${inventory.store} FAILED to scan: ${scanError}
|
|
4532
|
+
|
|
4533
|
+
`);
|
|
4534
|
+
outcomes.push({ store, status: "failed", events: 0, error: scanError, readFailures: 0 });
|
|
4535
|
+
return;
|
|
4536
|
+
}
|
|
4537
|
+
process.stdout.write(formatInventory(inventory) + "\n\n");
|
|
4538
|
+
if (!inventory.present) {
|
|
4539
|
+
outcomes.push({ store, status: "absent", events: 0, readFailures: 0 });
|
|
4540
|
+
return;
|
|
4541
|
+
}
|
|
4542
|
+
const events = [];
|
|
4543
|
+
const kindCounts = {};
|
|
4544
|
+
try {
|
|
4545
|
+
await extract((event) => {
|
|
4546
|
+
events.push(event);
|
|
4547
|
+
kindCounts[event.eventKind] = (kindCounts[event.eventKind] ?? 0) + 1;
|
|
4548
|
+
});
|
|
4549
|
+
} catch (error) {
|
|
4550
|
+
allEvents.push(...events);
|
|
4551
|
+
outcomes.push({
|
|
4552
|
+
store,
|
|
4553
|
+
status: "failed",
|
|
4554
|
+
events: events.length,
|
|
4555
|
+
error: error instanceof Error ? error.message : String(error),
|
|
4556
|
+
readFailures: readFailuresOf(events)
|
|
4557
|
+
});
|
|
4558
|
+
process.stdout.write(`${store} FAILED after ${events.length.toLocaleString("en-US")} events
|
|
4559
|
+
|
|
4560
|
+
`);
|
|
4561
|
+
return;
|
|
4562
|
+
}
|
|
4563
|
+
allEvents.push(...events);
|
|
4564
|
+
process.stdout.write(`${store} extracted: ${events.length.toLocaleString("en-US")} events
|
|
4565
|
+
`);
|
|
4566
|
+
for (const [kind, n] of Object.entries(kindCounts)) {
|
|
4567
|
+
process.stdout.write(` ${kind}: ${n.toLocaleString("en-US")}
|
|
4568
|
+
`);
|
|
4569
|
+
}
|
|
4570
|
+
const epoch = events.find((e) => e.eventKind === "extraction_epoch");
|
|
4571
|
+
if (epoch) {
|
|
4572
|
+
process.stdout.write(` window: ${epoch.metrics.windowOldest} \u2192 ${epoch.metrics.windowNewest}
|
|
4573
|
+
`);
|
|
4574
|
+
}
|
|
4575
|
+
const handoffPath = await writeHandoff(buildStoreHandoff(events));
|
|
4576
|
+
process.stdout.write(
|
|
4577
|
+
handoffPath ? ` handoff: \u2192 ${handoffPath}
|
|
4578
|
+
|
|
4579
|
+
` : " handoff: desktop app container not found \u2014 skipped\n\n"
|
|
4580
|
+
);
|
|
4581
|
+
outcomes.push({
|
|
4582
|
+
store,
|
|
4583
|
+
status: "extracted",
|
|
4584
|
+
events: events.length,
|
|
4585
|
+
readFailures: readFailuresOf(events)
|
|
4586
|
+
});
|
|
4587
|
+
}
|
|
4588
|
+
function writeSummary(input) {
|
|
4589
|
+
const { outcomes, shipResult, ship } = input;
|
|
4590
|
+
const out = process.stdout;
|
|
4591
|
+
out.write("\n" + "\u2500".repeat(60) + "\nsummary\n");
|
|
4592
|
+
for (const outcome of outcomes) {
|
|
4593
|
+
const name = outcome.store.padEnd(12);
|
|
4594
|
+
if (outcome.status === "absent") {
|
|
4595
|
+
out.write(` ${name} not present on this machine
|
|
4596
|
+
`);
|
|
4597
|
+
continue;
|
|
4598
|
+
}
|
|
4599
|
+
if (outcome.status === "failed") {
|
|
4600
|
+
out.write(` ${name} FAILED \u2014 ${outcome.error}
|
|
4601
|
+
`);
|
|
4602
|
+
out.write(` ${" ".repeat(12)} ${outcome.events.toLocaleString("en-US")} events salvaged before the failure
|
|
4603
|
+
`);
|
|
4604
|
+
continue;
|
|
4605
|
+
}
|
|
4606
|
+
const counts = shipResult?.perStore[outcome.store];
|
|
4607
|
+
let line = ` ${name} ${outcome.events.toLocaleString("en-US")} extracted`;
|
|
4608
|
+
if (counts) {
|
|
4609
|
+
line += `, ${counts.sent.toLocaleString("en-US")} sent`;
|
|
4610
|
+
line += ` (accepted ${counts.accepted.toLocaleString("en-US")}`;
|
|
4611
|
+
line += `, duplicate ${counts.duplicate.toLocaleString("en-US")}`;
|
|
4612
|
+
line += `, rejected ${counts.rejected.toLocaleString("en-US")})`;
|
|
4613
|
+
}
|
|
4614
|
+
out.write(line + "\n");
|
|
4615
|
+
if (outcome.readFailures > 0) {
|
|
4616
|
+
out.write(
|
|
4617
|
+
` ${" ".repeat(12)} \u26A0 ${outcome.readFailures.toLocaleString("en-US")} file(s)/record(s) this store could not read \u2014 the window is short by an unknown amount
|
|
4618
|
+
`
|
|
4619
|
+
);
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
out.write(` ${"total".padEnd(12)} ${input.totalExtracted.toLocaleString("en-US")} extracted`);
|
|
4623
|
+
out.write(ship ? `, ${input.shippable.toLocaleString("en-US")} eligible for the wire
|
|
4624
|
+
` : "\n");
|
|
4625
|
+
out.write(` events file: ${input.eventsFile}
|
|
4626
|
+
`);
|
|
4627
|
+
if (!ship) {
|
|
4628
|
+
out.write("\ndry run \u2014 pass --ship to send these over the batch wire.\n");
|
|
4629
|
+
return;
|
|
4630
|
+
}
|
|
4631
|
+
if (input.shipError) {
|
|
4632
|
+
out.write(`
|
|
4633
|
+
ship FAILED: ${input.shipError}
|
|
4634
|
+
`);
|
|
4635
|
+
return;
|
|
4636
|
+
}
|
|
4637
|
+
if (!shipResult) {
|
|
4638
|
+
out.write("\nship did not run.\n");
|
|
4639
|
+
return;
|
|
4640
|
+
}
|
|
4641
|
+
out.write(
|
|
4642
|
+
`
|
|
4643
|
+
shipped: sent=${shipResult.sent} accepted=${shipResult.accepted} duplicate=${shipResult.duplicate} rejected=${shipResult.rejected} httpFailures=${shipResult.httpFailures}
|
|
4644
|
+
`
|
|
4645
|
+
);
|
|
4646
|
+
if (shipResult.duplicate > 0) {
|
|
4647
|
+
out.write(
|
|
4648
|
+
` ${shipResult.duplicate.toLocaleString("en-US")} already imported \u2014 the backend kept its existing copy and stored nothing new.
|
|
4649
|
+
`
|
|
4650
|
+
);
|
|
4651
|
+
}
|
|
4652
|
+
if (shipResult.consentBlocked) {
|
|
4653
|
+
out.write(
|
|
4654
|
+
"\n stopped early: this account has no consent for a retrospective import, so the\n backend refused the whole batch and every remaining event would be refused too.\n Nothing was stored. Grant it in Flow \u2014 Connections > Bring in daily tools \u2014 and\n run this again; the import is idempotent, so nothing here is wasted.\n"
|
|
4655
|
+
);
|
|
4656
|
+
}
|
|
4657
|
+
for (const [reason, n] of Object.entries(shipResult.rejectionReasons)) {
|
|
4658
|
+
out.write(` rejected ${reason}: ${n}
|
|
4659
|
+
`);
|
|
4660
|
+
}
|
|
4661
|
+
if (!shipResult.attributionComplete) {
|
|
4662
|
+
out.write(
|
|
4663
|
+
" note: at least one batch could not be attributed per source \u2014 the per-source accept/reject splits above are incomplete.\n"
|
|
4664
|
+
);
|
|
4665
|
+
}
|
|
4666
|
+
}
|
|
4667
|
+
function exitCodeFor(outcomes, ship, shipResult, shipError) {
|
|
4668
|
+
if (outcomes.some((o) => o.status === "failed")) return 1;
|
|
4669
|
+
if (!ship) return 0;
|
|
4670
|
+
if (shipError || !shipResult) return 1;
|
|
4671
|
+
if (shipResult.rejected > 0 || shipResult.httpFailures > 0) return 1;
|
|
4672
|
+
return shipResult.accepted + shipResult.duplicate > 0 ? 0 : 1;
|
|
4673
|
+
}
|
|
3685
4674
|
async function main() {
|
|
3686
4675
|
const [command, ...rest] = process.argv.slice(2);
|
|
3687
4676
|
const paths = resolveStorePaths();
|
|
@@ -3697,158 +4686,258 @@ async function main() {
|
|
|
3697
4686
|
}
|
|
3698
4687
|
case "import": {
|
|
3699
4688
|
const ship = rest.includes("--ship");
|
|
3700
|
-
const
|
|
3701
|
-
const
|
|
3702
|
-
const
|
|
3703
|
-
|
|
4689
|
+
const keepStaging = rest.includes("--keep-staging");
|
|
4690
|
+
const snapshotSessions = rest.includes("--snapshot-sessions");
|
|
4691
|
+
const claudeScan = await safeScan("claude_code", paths.claudeProjects, () => scanClaudeCode(paths));
|
|
4692
|
+
const cursorScan = await safeScan("cursor", paths.cursorStateDb, () => scanCursor(paths));
|
|
4693
|
+
const vsCodeScan = await safeScan("vscode", paths.vscodeHistory, () => scanVsCode(paths));
|
|
4694
|
+
const claudeInventory = claudeScan.inventory;
|
|
4695
|
+
const cursorInventory = cursorScan.inventory;
|
|
4696
|
+
const vsCodeInventory = vsCodeScan.inventory;
|
|
4697
|
+
const anyScanFailed = [claudeScan, cursorScan, vsCodeScan].some((r) => r.scanError !== null);
|
|
4698
|
+
if (!claudeInventory.present && !cursorInventory.present && !vsCodeInventory.present && !anyScanFailed) {
|
|
3704
4699
|
process.stderr.write(
|
|
3705
4700
|
`no Claude Code store at ${claudeInventory.rootPath}, no Cursor store at ${cursorInventory.rootPath}, and no VS Code store at ${vsCodeInventory.rootPath} \u2014 nothing to import
|
|
3706
4701
|
`
|
|
3707
4702
|
);
|
|
3708
4703
|
return 2;
|
|
3709
4704
|
}
|
|
3710
|
-
const
|
|
4705
|
+
const stagingRoot = defaultStagingRoot(paths.home);
|
|
4706
|
+
const swept = await sweepStagingRoot(stagingRoot);
|
|
4707
|
+
if (swept.runsSwept > 0) {
|
|
4708
|
+
process.stdout.write(
|
|
4709
|
+
`swept ${swept.runsSwept} stale staging run(s), freed ${formatBytes(swept.freedBytes)}
|
|
4710
|
+
`
|
|
4711
|
+
);
|
|
4712
|
+
}
|
|
4713
|
+
const willCopy = [paths.claudeProjects, paths.cursorStateDb, paths.vscodeHistory].filter(
|
|
4714
|
+
(_, i) => [claudeInventory.present, cursorInventory.present, vsCodeInventory.present][i]
|
|
4715
|
+
);
|
|
4716
|
+
if (snapshotSessions && vsCodeInventory.present) willCopy.push(paths.vscodeWorkspaceStorage);
|
|
4717
|
+
const space = await checkSpaceForSnapshot(stagingRoot, willCopy);
|
|
4718
|
+
if (!space.sufficient) {
|
|
4719
|
+
process.stderr.write(
|
|
4720
|
+
`not enough space to stage this import: need ~${formatBytes(space.requiredBytes)}, ${formatBytes(space.freeBytes ?? 0)} free on the volume holding ${stagingRoot}
|
|
4721
|
+
refusing to start rather than failing halfway through the copy
|
|
4722
|
+
`
|
|
4723
|
+
);
|
|
4724
|
+
return 2;
|
|
4725
|
+
}
|
|
4726
|
+
const area = await createStagingArea(stagingRoot);
|
|
3711
4727
|
process.stdout.write(`staging: ${area.root}
|
|
3712
4728
|
|
|
3713
4729
|
`);
|
|
3714
4730
|
const allEvents = [];
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
4731
|
+
const outcomes = [];
|
|
4732
|
+
try {
|
|
4733
|
+
await runSource(outcomes, "claude_code", claudeInventory, allEvents, async (collect) => {
|
|
4734
|
+
const snapshotRoot = path9.join(area.root, "claude_code");
|
|
4735
|
+
await snapshotPath(area, paths.claudeProjects, path9.join("claude_code", "projects"));
|
|
4736
|
+
for await (const event of extractClaudeCode(snapshotRoot, area.extractionId)) collect(event);
|
|
4737
|
+
}, (events) => buildHandoff(events, area.extractionId, (/* @__PURE__ */ new Date()).toISOString()), claudeScan.scanError);
|
|
4738
|
+
await runSource(outcomes, "cursor", cursorInventory, allEvents, async (collect) => {
|
|
4739
|
+
const cursorSnapshotRoot = path9.join(area.root, "cursor");
|
|
4740
|
+
await snapshotPath(area, paths.cursorStateDb, path9.join("cursor", "state.vscdb"));
|
|
4741
|
+
for await (const event of extractCursor(cursorSnapshotRoot, area.extractionId)) collect(event);
|
|
4742
|
+
}, (events) => buildCursorHandoff(events, area.extractionId, (/* @__PURE__ */ new Date()).toISOString()), cursorScan.scanError);
|
|
4743
|
+
await runSource(outcomes, "vscode", vsCodeInventory, allEvents, async (collect) => {
|
|
4744
|
+
await snapshotPath(area, paths.vscodeHistory, path9.join("vscode", "history"));
|
|
4745
|
+
let workspaceStorageDir = paths.vscodeWorkspaceStorage;
|
|
4746
|
+
if (snapshotSessions) {
|
|
4747
|
+
workspaceStorageDir = await snapshotVsCodeWorkspaceStorage(
|
|
4748
|
+
area,
|
|
4749
|
+
paths.vscodeWorkspaceStorage,
|
|
4750
|
+
path9.join("vscode", "workspaceStorage")
|
|
4751
|
+
);
|
|
4752
|
+
}
|
|
4753
|
+
const source = {
|
|
4754
|
+
historyDir: path9.join(area.root, "vscode", "history"),
|
|
4755
|
+
workspaceStorageDir
|
|
4756
|
+
};
|
|
4757
|
+
for await (const event of extractVsCode(source, area.extractionId)) collect(event);
|
|
4758
|
+
}, (events) => buildVsCodeHandoff(events, area.extractionId, (/* @__PURE__ */ new Date()).toISOString()), vsCodeScan.scanError);
|
|
4759
|
+
const eventsFile = path9.join(area.root, "events.jsonl");
|
|
4760
|
+
await fs9.writeFile(eventsFile, allEvents.map((e) => JSON.stringify(e)).join("\n") + "\n", "utf8");
|
|
4761
|
+
let shipResult = null;
|
|
4762
|
+
let shipError = null;
|
|
4763
|
+
if (ship) {
|
|
4764
|
+
const config = loadShipConfig();
|
|
4765
|
+
process.stdout.write(`
|
|
4766
|
+
shipping to ${config.apiBaseUrl} as ${config.toolInstallationId}\u2026
|
|
3735
4767
|
`);
|
|
4768
|
+
try {
|
|
4769
|
+
shipResult = await shipEvents(allEvents, config, (done, total) => {
|
|
4770
|
+
process.stdout.write(` ${done.toLocaleString("en-US")}/${total.toLocaleString("en-US")}\r`);
|
|
4771
|
+
});
|
|
4772
|
+
process.stdout.write("\n");
|
|
4773
|
+
} catch (error) {
|
|
4774
|
+
process.stdout.write("\n");
|
|
4775
|
+
shipError = error instanceof Error ? error.message : String(error);
|
|
4776
|
+
}
|
|
3736
4777
|
}
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
4778
|
+
writeSummary({
|
|
4779
|
+
outcomes,
|
|
4780
|
+
eventsFile,
|
|
4781
|
+
totalExtracted: allEvents.length,
|
|
4782
|
+
shippable: shippableEvents(allEvents).length,
|
|
4783
|
+
ship,
|
|
4784
|
+
shipResult,
|
|
4785
|
+
shipError
|
|
4786
|
+
});
|
|
4787
|
+
return exitCodeFor(outcomes, ship, shipResult, shipError);
|
|
4788
|
+
} finally {
|
|
4789
|
+
if (keepStaging) {
|
|
4790
|
+
process.stdout.write(`
|
|
4791
|
+
staging kept at ${area.root} (--keep-staging)
|
|
3742
4792
|
`);
|
|
3743
4793
|
} else {
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
await snapshotPath(area, paths.cursorStateDb, path8.join("cursor", "state.vscdb"));
|
|
3753
|
-
const cursorEvents = [];
|
|
3754
|
-
const kindCounts = {};
|
|
3755
|
-
for await (const event of extractCursor(cursorSnapshotRoot, area.extractionId)) {
|
|
3756
|
-
cursorEvents.push(event);
|
|
3757
|
-
kindCounts[event.eventKind] = (kindCounts[event.eventKind] ?? 0) + 1;
|
|
4794
|
+
const disposed = await disposeStagingArea(area);
|
|
4795
|
+
if (disposed.freedBytes > 0) {
|
|
4796
|
+
process.stdout.write(
|
|
4797
|
+
`
|
|
4798
|
+
staging cleaned: freed ${formatBytes(disposed.freedBytes)}, kept ${disposed.kept.join(", ") || "nothing"}
|
|
4799
|
+
`
|
|
4800
|
+
);
|
|
4801
|
+
}
|
|
3758
4802
|
}
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
4803
|
+
}
|
|
4804
|
+
}
|
|
4805
|
+
case "archive": {
|
|
4806
|
+
const archiveRoot = defaultArchiveRoot(paths.home);
|
|
4807
|
+
const flagValue = (name) => {
|
|
4808
|
+
const at = rest.indexOf(name);
|
|
4809
|
+
return at >= 0 && rest[at + 1] ? rest[at + 1] : null;
|
|
4810
|
+
};
|
|
4811
|
+
const generationFlag = flagValue("--generation");
|
|
4812
|
+
if (rest.includes("--list")) {
|
|
4813
|
+
const generations = await listManifests(archiveRoot);
|
|
4814
|
+
if (generations.length === 0) {
|
|
4815
|
+
process.stdout.write(`no archive at ${archiveRoot} \u2014 run \`archive\` to create one
|
|
3765
4816
|
`);
|
|
4817
|
+
return 0;
|
|
3766
4818
|
}
|
|
3767
|
-
|
|
3768
|
-
|
|
4819
|
+
for (const generation2 of generations) {
|
|
4820
|
+
const manifest = await readManifest(archiveRoot, generation2);
|
|
4821
|
+
if (!manifest) {
|
|
4822
|
+
process.stdout.write(` ${generation2} UNREADABLE
|
|
3769
4823
|
`);
|
|
4824
|
+
continue;
|
|
4825
|
+
}
|
|
4826
|
+
const bytes = manifest.files.reduce((sum, f) => sum + f.size, 0);
|
|
4827
|
+
process.stdout.write(
|
|
4828
|
+
` ${generation2} ${manifest.files.length.toLocaleString("en-US")} files ${formatBytes(bytes)} logical
|
|
4829
|
+
`
|
|
4830
|
+
);
|
|
3770
4831
|
}
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
if (handoffPath) {
|
|
3774
|
-
process.stdout.write(` handoff: ${handoff.sessions.length} sessions \u2192 ${handoffPath}
|
|
3775
|
-
|
|
4832
|
+
process.stdout.write(`
|
|
4833
|
+
archive on disk: ${formatBytes(await archiveSizeBytes(archiveRoot))}
|
|
3776
4834
|
`);
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
const vsCodeSnapshotRoot = path8.join(area.root, "vscode");
|
|
3786
|
-
await snapshotPath(area, paths.vscodeHistory, path8.join("vscode", "history"));
|
|
3787
|
-
await snapshotVsCodeWorkspaceStorage(area, paths.vscodeWorkspaceStorage, path8.join("vscode", "workspaceStorage"));
|
|
3788
|
-
const vsCodeEvents = [];
|
|
3789
|
-
const kindCounts = {};
|
|
3790
|
-
for await (const event of extractVsCode(vsCodeSnapshotRoot, area.extractionId)) {
|
|
3791
|
-
vsCodeEvents.push(event);
|
|
3792
|
-
kindCounts[event.eventKind] = (kindCounts[event.eventKind] ?? 0) + 1;
|
|
4835
|
+
return 0;
|
|
4836
|
+
}
|
|
4837
|
+
if (rest.includes("--verify")) {
|
|
4838
|
+
const manifest = generationFlag ? await readManifest(archiveRoot, generationFlag) : await readLatestManifest(archiveRoot);
|
|
4839
|
+
if (!manifest) {
|
|
4840
|
+
process.stderr.write(`no readable generation to verify in ${archiveRoot}
|
|
4841
|
+
`);
|
|
4842
|
+
return 2;
|
|
3793
4843
|
}
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
4844
|
+
const verified = await verifyArchive(archiveRoot, manifest);
|
|
4845
|
+
process.stdout.write(
|
|
4846
|
+
`generation ${verified.generation}: ${verified.checked.toLocaleString("en-US")} files checked, ${verified.missing.length} missing, ${verified.corrupted.length} corrupted
|
|
4847
|
+
`
|
|
4848
|
+
);
|
|
4849
|
+
for (const file of verified.missing.slice(0, 10)) process.stdout.write(` missing: ${file}
|
|
3797
4850
|
`);
|
|
3798
|
-
for (const
|
|
3799
|
-
process.stdout.write(` ${kind}: ${n.toLocaleString("en-US")}
|
|
4851
|
+
for (const file of verified.corrupted.slice(0, 10)) process.stdout.write(` corrupted: ${file}
|
|
3800
4852
|
`);
|
|
4853
|
+
return verified.missing.length + verified.corrupted.length > 0 ? 1 : 0;
|
|
4854
|
+
}
|
|
4855
|
+
const restoreTo = flagValue("--restore");
|
|
4856
|
+
if (rest.includes("--restore")) {
|
|
4857
|
+
if (!restoreTo) {
|
|
4858
|
+
process.stderr.write("--restore needs a destination directory\n");
|
|
4859
|
+
return 2;
|
|
3801
4860
|
}
|
|
3802
|
-
|
|
3803
|
-
|
|
4861
|
+
const manifest = generationFlag ? await readManifest(archiveRoot, generationFlag) : await readLatestManifest(archiveRoot);
|
|
4862
|
+
if (!manifest) {
|
|
4863
|
+
process.stderr.write(`no readable generation to restore in ${archiveRoot}
|
|
3804
4864
|
`);
|
|
4865
|
+
return 2;
|
|
3805
4866
|
}
|
|
3806
|
-
const
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
process.stdout.write(
|
|
3810
|
-
` handoff: ${handoff.editDays.length} edit-days, ${handoff.chatSessions.length} chat sessions \u2192 ${handoffPath}
|
|
3811
|
-
|
|
4867
|
+
const restored = await restoreArchive(archiveRoot, manifest, restoreTo);
|
|
4868
|
+
process.stdout.write(
|
|
4869
|
+
`restored ${restored.restored.toLocaleString("en-US")} files from ${manifest.generation} \u2192 ${restored.destination}
|
|
3812
4870
|
`
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
process.stdout.write(
|
|
4871
|
+
);
|
|
4872
|
+
if (restored.skipped > 0) {
|
|
4873
|
+
process.stdout.write(` ${restored.skipped} could not be written
|
|
4874
|
+
`);
|
|
3816
4875
|
}
|
|
3817
|
-
|
|
3818
|
-
|
|
4876
|
+
process.stdout.write(
|
|
4877
|
+
"\nnothing was written to the live stores \u2014 copy back by hand once you have looked at this.\n"
|
|
4878
|
+
);
|
|
4879
|
+
return restored.skipped > 0 ? 1 : 0;
|
|
3819
4880
|
}
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
4881
|
+
if (rest.includes("--prune")) {
|
|
4882
|
+
const keep = Number(flagValue("--keep") ?? 10);
|
|
4883
|
+
if (!Number.isInteger(keep) || keep < 1) {
|
|
4884
|
+
process.stderr.write("--keep needs a positive integer\n");
|
|
4885
|
+
return 2;
|
|
4886
|
+
}
|
|
4887
|
+
const pruned = await pruneArchive(archiveRoot, keep);
|
|
4888
|
+
process.stdout.write(
|
|
4889
|
+
`pruned ${pruned.generationsRemoved.length} generation(s) and ${pruned.blobsRemoved} unreferenced blob(s), freed ${formatBytes(pruned.freedBytes)}
|
|
4890
|
+
`
|
|
4891
|
+
);
|
|
4892
|
+
process.stdout.write(`archive on disk: ${formatBytes(await archiveSizeBytes(archiveRoot))}
|
|
3825
4893
|
`);
|
|
3826
|
-
if (!ship) {
|
|
3827
|
-
process.stdout.write("dry run \u2014 pass --ship to send these over the batch wire.\n");
|
|
3828
4894
|
return 0;
|
|
3829
4895
|
}
|
|
3830
|
-
const
|
|
3831
|
-
|
|
4896
|
+
const includeSessions = rest.includes("--include-vscode-sessions");
|
|
4897
|
+
const sources = defaultArchiveSources(paths, includeSessions);
|
|
4898
|
+
const generation = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4899
|
+
process.stdout.write(`archiving to ${archiveRoot}
|
|
3832
4900
|
`);
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
4901
|
+
for (const source of sources) process.stdout.write(` ${source.store}/${source.label}: ${source.root}
|
|
4902
|
+
`);
|
|
4903
|
+
if (!includeSessions) {
|
|
4904
|
+
process.stdout.write(
|
|
4905
|
+
" vscode/workspaceStorage: skipped \u2014 15 GB that VS Code is not deleting (--include-vscode-sessions to add)\n"
|
|
4906
|
+
);
|
|
4907
|
+
}
|
|
3836
4908
|
process.stdout.write("\n");
|
|
4909
|
+
const result = await archiveStores({
|
|
4910
|
+
archiveRoot,
|
|
4911
|
+
sources,
|
|
4912
|
+
generation,
|
|
4913
|
+
now: (/* @__PURE__ */ new Date()).toISOString()
|
|
4914
|
+
});
|
|
4915
|
+
process.stdout.write("\u2500".repeat(60) + `
|
|
4916
|
+
generation ${result.generation}
|
|
4917
|
+
`);
|
|
4918
|
+
for (const [store, counts] of Object.entries(result.perStore)) {
|
|
4919
|
+
process.stdout.write(
|
|
4920
|
+
` ${store.padEnd(12)} ${counts.files.toLocaleString("en-US")} files, ${formatBytes(counts.newBytes)} new
|
|
4921
|
+
`
|
|
4922
|
+
);
|
|
4923
|
+
}
|
|
3837
4924
|
process.stdout.write(
|
|
3838
|
-
`
|
|
4925
|
+
` ${"total".padEnd(12)} ${result.filesArchived.toLocaleString("en-US")} files (${result.filesDeduplicated.toLocaleString("en-US")} already held), ${formatBytes(result.newBytes)} new
|
|
3839
4926
|
`
|
|
3840
4927
|
);
|
|
3841
|
-
if (result.
|
|
4928
|
+
if (result.unreadable > 0) {
|
|
3842
4929
|
process.stdout.write(
|
|
3843
|
-
` ${result.
|
|
4930
|
+
` \u26A0 ${result.unreadable} file(s) could not be read \u2014 this generation is short by an unknown amount
|
|
3844
4931
|
`
|
|
3845
4932
|
);
|
|
3846
4933
|
}
|
|
3847
|
-
|
|
3848
|
-
|
|
4934
|
+
const onDisk = await archiveSizeBytes(archiveRoot);
|
|
4935
|
+
process.stdout.write(` archive on disk: ${formatBytes(onDisk)} across ${(await listManifests(archiveRoot)).length} generation(s)
|
|
3849
4936
|
`);
|
|
3850
|
-
}
|
|
3851
|
-
|
|
4937
|
+
process.stdout.write(` manifest: ${result.manifestPath}
|
|
4938
|
+
`);
|
|
4939
|
+
process.stdout.write("\nverify it: ascenda-history-import archive --verify\n");
|
|
4940
|
+
return result.unreadable > 0 ? 1 : 0;
|
|
3852
4941
|
}
|
|
3853
4942
|
case "fix-retention": {
|
|
3854
4943
|
const plan = await planClaudeRetentionFix(paths);
|
|
@@ -3873,7 +4962,7 @@ async function main() {
|
|
|
3873
4962
|
}
|
|
3874
4963
|
default: {
|
|
3875
4964
|
process.stderr.write(
|
|
3876
|
-
"usage: ascenda-history-import <scan [--json] | import [--ship] | fix-retention [--apply]>\n"
|
|
4965
|
+
"usage: ascenda-history-import <scan [--json] | import [--ship] | fix-retention [--apply] | archive [--verify|--list|--restore <dir>|--prune]>\n"
|
|
3877
4966
|
);
|
|
3878
4967
|
return command === void 0 || command === "--help" ? 0 : 2;
|
|
3879
4968
|
}
|