@ascenda-one/github-collector 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +562 -46
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -415,6 +415,93 @@ var require_out = __commonJS({
|
|
|
415
415
|
}
|
|
416
416
|
});
|
|
417
417
|
|
|
418
|
+
// ../packages/tool-kit/out/eventLog.js
|
|
419
|
+
var require_eventLog = __commonJS({
|
|
420
|
+
"../packages/tool-kit/out/eventLog.js"(exports) {
|
|
421
|
+
"use strict";
|
|
422
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
423
|
+
if (k2 === void 0) k2 = k;
|
|
424
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
425
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
426
|
+
desc = { enumerable: true, get: function() {
|
|
427
|
+
return m[k];
|
|
428
|
+
} };
|
|
429
|
+
}
|
|
430
|
+
Object.defineProperty(o, k2, desc);
|
|
431
|
+
} : function(o, m, k, k2) {
|
|
432
|
+
if (k2 === void 0) k2 = k;
|
|
433
|
+
o[k2] = m[k];
|
|
434
|
+
});
|
|
435
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
436
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
437
|
+
} : function(o, v) {
|
|
438
|
+
o["default"] = v;
|
|
439
|
+
});
|
|
440
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
441
|
+
var ownKeys = function(o) {
|
|
442
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
443
|
+
var ar = [];
|
|
444
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
445
|
+
return ar;
|
|
446
|
+
};
|
|
447
|
+
return ownKeys(o);
|
|
448
|
+
};
|
|
449
|
+
return function(mod) {
|
|
450
|
+
if (mod && mod.__esModule) return mod;
|
|
451
|
+
var result = {};
|
|
452
|
+
if (mod != null) {
|
|
453
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
454
|
+
}
|
|
455
|
+
__setModuleDefault(result, mod);
|
|
456
|
+
return result;
|
|
457
|
+
};
|
|
458
|
+
}();
|
|
459
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
460
|
+
exports.EVENT_LOG_ENV_VAR = void 0;
|
|
461
|
+
exports.expandUserPath = expandUserPath;
|
|
462
|
+
exports.resolveEventLogPath = resolveEventLogPath;
|
|
463
|
+
exports.appendEventLog = appendEventLog;
|
|
464
|
+
var fs = __importStar(__require("fs"));
|
|
465
|
+
var os = __importStar(__require("os"));
|
|
466
|
+
var path = __importStar(__require("path"));
|
|
467
|
+
exports.EVENT_LOG_ENV_VAR = "ASCENDA_EVENT_LOG_FILE";
|
|
468
|
+
var MAX_BYTES = 5 * 1024 * 1024;
|
|
469
|
+
function expandUserPath(configured) {
|
|
470
|
+
const value = configured?.trim();
|
|
471
|
+
if (!value)
|
|
472
|
+
return void 0;
|
|
473
|
+
if (value === "~")
|
|
474
|
+
return os.homedir();
|
|
475
|
+
if (value.startsWith("~/"))
|
|
476
|
+
return path.join(os.homedir(), value.slice(2));
|
|
477
|
+
return path.resolve(value);
|
|
478
|
+
}
|
|
479
|
+
function resolveEventLogPath() {
|
|
480
|
+
return expandUserPath(process.env[exports.EVENT_LOG_ENV_VAR]);
|
|
481
|
+
}
|
|
482
|
+
function appendEventLog(logFilePath, entry) {
|
|
483
|
+
try {
|
|
484
|
+
rotateIfLarge(logFilePath);
|
|
485
|
+
const dir = path.dirname(logFilePath);
|
|
486
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
487
|
+
fs.appendFileSync(logFilePath, `${JSON.stringify(entry)}
|
|
488
|
+
`, { encoding: "utf8", mode: 384 });
|
|
489
|
+
if (process.platform !== "win32")
|
|
490
|
+
fs.chmodSync(logFilePath, 384);
|
|
491
|
+
} catch {
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function rotateIfLarge(logFilePath) {
|
|
495
|
+
try {
|
|
496
|
+
if (fs.statSync(logFilePath).size < MAX_BYTES)
|
|
497
|
+
return;
|
|
498
|
+
fs.renameSync(logFilePath, `${logFilePath}.1`);
|
|
499
|
+
} catch {
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
|
|
418
505
|
// ../packages/tool-kit/out/http.js
|
|
419
506
|
var require_http = __commonJS({
|
|
420
507
|
"../packages/tool-kit/out/http.js"(exports) {
|
|
@@ -424,6 +511,7 @@ var require_http = __commonJS({
|
|
|
424
511
|
exports.createPairingSession = createPairingSession;
|
|
425
512
|
exports.getPairingStatus = getPairingStatus;
|
|
426
513
|
exports.renewToolToken = renewToolToken;
|
|
514
|
+
exports.isRetryableStatus = isRetryableStatus;
|
|
427
515
|
exports.postToolEvent = postToolEvent;
|
|
428
516
|
exports.postToolEventsBatch = postToolEventsBatch;
|
|
429
517
|
exports.parseIngestResponse = parseIngestResponse;
|
|
@@ -469,26 +557,34 @@ var require_http = __commonJS({
|
|
|
469
557
|
throw new AscendaApiError(response.status, void 0, await response.text());
|
|
470
558
|
return await response.json();
|
|
471
559
|
}
|
|
560
|
+
function isRetryableStatus(status) {
|
|
561
|
+
return status === 408 || status === 429 || status !== void 0 && status >= 500 && status <= 599;
|
|
562
|
+
}
|
|
472
563
|
async function postToolEvent(apiBaseUrl, eventWriteToken, payload, signal) {
|
|
473
|
-
|
|
474
|
-
method: "POST",
|
|
475
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${eventWriteToken}` },
|
|
476
|
-
body: JSON.stringify(payload),
|
|
477
|
-
signal
|
|
478
|
-
});
|
|
479
|
-
return parseIngestResponse(response);
|
|
564
|
+
return sendIngest(`${apiBaseUrl}/v1/tool-events`, eventWriteToken, JSON.stringify(payload), signal);
|
|
480
565
|
}
|
|
481
|
-
async function postToolEventsBatch(apiBaseUrl, eventWriteToken, payloads) {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
566
|
+
async function postToolEventsBatch(apiBaseUrl, eventWriteToken, payloads, signal) {
|
|
567
|
+
return sendIngest(`${apiBaseUrl}/v1/tool-events/batch`, eventWriteToken, JSON.stringify({ events: payloads }), signal);
|
|
568
|
+
}
|
|
569
|
+
async function sendIngest(url, eventWriteToken, body, signal) {
|
|
570
|
+
try {
|
|
571
|
+
const response = await fetch(url, {
|
|
572
|
+
method: "POST",
|
|
573
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${eventWriteToken}` },
|
|
574
|
+
body,
|
|
575
|
+
signal
|
|
576
|
+
});
|
|
577
|
+
return parseIngestResponse(response);
|
|
578
|
+
} catch (error) {
|
|
579
|
+
return {
|
|
580
|
+
result: "transport_error",
|
|
581
|
+
detail: error instanceof Error ? `${error.name}: ${error.message}` : String(error)
|
|
582
|
+
};
|
|
583
|
+
}
|
|
488
584
|
}
|
|
489
585
|
async function parseIngestResponse(response) {
|
|
490
586
|
if (response.ok)
|
|
491
|
-
return "accepted";
|
|
587
|
+
return { result: "accepted", httpStatus: response.status };
|
|
492
588
|
const body = await response.text();
|
|
493
589
|
let errorCode;
|
|
494
590
|
try {
|
|
@@ -496,13 +592,14 @@ var require_http = __commonJS({
|
|
|
496
592
|
} catch {
|
|
497
593
|
errorCode = void 0;
|
|
498
594
|
}
|
|
595
|
+
const base2 = { httpStatus: response.status, errorCode, detail: body || void 0 };
|
|
499
596
|
if (response.status === 401)
|
|
500
|
-
return "auth_failed";
|
|
597
|
+
return { ...base2, result: "auth_failed" };
|
|
501
598
|
if (response.status === 403 && errorCode === "consent_missing_or_expired")
|
|
502
|
-
return "consent_missing";
|
|
599
|
+
return { ...base2, result: "consent_missing" };
|
|
503
600
|
if (response.status === 400 || response.status === 422)
|
|
504
|
-
return "validation_failed";
|
|
505
|
-
|
|
601
|
+
return { ...base2, result: "validation_failed" };
|
|
602
|
+
return { ...base2, result: "transport_error" };
|
|
506
603
|
}
|
|
507
604
|
}
|
|
508
605
|
});
|
|
@@ -552,6 +649,7 @@ var require_tokenStore = __commonJS({
|
|
|
552
649
|
exports.defaultTokenFilePath = defaultTokenFilePath2;
|
|
553
650
|
exports.persistEventWriteToken = persistEventWriteToken2;
|
|
554
651
|
exports.readTokenFile = readTokenFile2;
|
|
652
|
+
exports.sanitizeFilePart = sanitizeFilePart;
|
|
555
653
|
var fs = __importStar(__require("fs"));
|
|
556
654
|
var os = __importStar(__require("os"));
|
|
557
655
|
var path = __importStar(__require("path"));
|
|
@@ -583,15 +681,144 @@ var require_tokenStore = __commonJS({
|
|
|
583
681
|
}
|
|
584
682
|
});
|
|
585
683
|
|
|
684
|
+
// ../packages/tool-kit/out/stateStore.js
|
|
685
|
+
var require_stateStore = __commonJS({
|
|
686
|
+
"../packages/tool-kit/out/stateStore.js"(exports) {
|
|
687
|
+
"use strict";
|
|
688
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
689
|
+
if (k2 === void 0) k2 = k;
|
|
690
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
691
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
692
|
+
desc = { enumerable: true, get: function() {
|
|
693
|
+
return m[k];
|
|
694
|
+
} };
|
|
695
|
+
}
|
|
696
|
+
Object.defineProperty(o, k2, desc);
|
|
697
|
+
} : function(o, m, k, k2) {
|
|
698
|
+
if (k2 === void 0) k2 = k;
|
|
699
|
+
o[k2] = m[k];
|
|
700
|
+
});
|
|
701
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
702
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
703
|
+
} : function(o, v) {
|
|
704
|
+
o["default"] = v;
|
|
705
|
+
});
|
|
706
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
707
|
+
var ownKeys = function(o) {
|
|
708
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
709
|
+
var ar = [];
|
|
710
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
711
|
+
return ar;
|
|
712
|
+
};
|
|
713
|
+
return ownKeys(o);
|
|
714
|
+
};
|
|
715
|
+
return function(mod) {
|
|
716
|
+
if (mod && mod.__esModule) return mod;
|
|
717
|
+
var result = {};
|
|
718
|
+
if (mod != null) {
|
|
719
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
720
|
+
}
|
|
721
|
+
__setModuleDefault(result, mod);
|
|
722
|
+
return result;
|
|
723
|
+
};
|
|
724
|
+
}();
|
|
725
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
726
|
+
exports.defaultStateFilePath = defaultStateFilePath;
|
|
727
|
+
exports.readCollectorState = readCollectorState;
|
|
728
|
+
exports.recordSendOutcome = recordSendOutcome;
|
|
729
|
+
exports.shouldAnnounceFailure = shouldAnnounceFailure;
|
|
730
|
+
exports.markFailureNotified = markFailureNotified;
|
|
731
|
+
var fs = __importStar(__require("fs"));
|
|
732
|
+
var os = __importStar(__require("os"));
|
|
733
|
+
var path = __importStar(__require("path"));
|
|
734
|
+
var tokenStore_1 = require_tokenStore();
|
|
735
|
+
function defaultStateFilePath(toolInstallationId) {
|
|
736
|
+
return path.join(os.homedir(), ".ascenda", "state", `${(0, tokenStore_1.sanitizeFilePart)(toolInstallationId)}.json`);
|
|
737
|
+
}
|
|
738
|
+
function readCollectorState(stateFilePath) {
|
|
739
|
+
try {
|
|
740
|
+
if (!fs.existsSync(stateFilePath))
|
|
741
|
+
return void 0;
|
|
742
|
+
const raw = fs.readFileSync(stateFilePath, "utf8").trim();
|
|
743
|
+
if (!raw)
|
|
744
|
+
return void 0;
|
|
745
|
+
const parsed = JSON.parse(raw);
|
|
746
|
+
if (!parsed || typeof parsed !== "object")
|
|
747
|
+
return void 0;
|
|
748
|
+
return parsed;
|
|
749
|
+
} catch {
|
|
750
|
+
return void 0;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
function recordSendOutcome(stateFilePath, toolInstallationId, outcome, detail = {}) {
|
|
754
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
755
|
+
const previous = readCollectorState(stateFilePath);
|
|
756
|
+
const accepted = outcome === "accepted";
|
|
757
|
+
const next = {
|
|
758
|
+
toolInstallationId,
|
|
759
|
+
lastAttemptAt: now,
|
|
760
|
+
lastSuccessAt: accepted ? now : previous?.lastSuccessAt,
|
|
761
|
+
lastOutcome: outcome,
|
|
762
|
+
consecutiveFailures: accepted ? 0 : (previous?.consecutiveFailures ?? 0) + 1,
|
|
763
|
+
...detail.httpStatus !== void 0 ? { httpStatus: detail.httpStatus } : {},
|
|
764
|
+
...detail.errorCode !== void 0 ? { errorCode: detail.errorCode } : {},
|
|
765
|
+
...detail.detail !== void 0 ? { detail: truncate(detail.detail) } : {},
|
|
766
|
+
// A success closes the episode; a failure either opens one or continues
|
|
767
|
+
// the one already open. Carrying `notifiedFailingSince` across a
|
|
768
|
+
// continuing episode is what keeps the notice to once per outage.
|
|
769
|
+
...accepted ? {} : { failingSince: previous?.failingSince ?? now },
|
|
770
|
+
...accepted ? {} : previous?.notifiedFailingSince !== void 0 ? { notifiedFailingSince: previous.notifiedFailingSince } : {}
|
|
771
|
+
};
|
|
772
|
+
writeStateFile(stateFilePath, next);
|
|
773
|
+
return next;
|
|
774
|
+
}
|
|
775
|
+
function shouldAnnounceFailure(state) {
|
|
776
|
+
if (!state || state.consecutiveFailures === 0 || !state.failingSince)
|
|
777
|
+
return false;
|
|
778
|
+
return state.notifiedFailingSince !== state.failingSince;
|
|
779
|
+
}
|
|
780
|
+
function markFailureNotified(stateFilePath, state) {
|
|
781
|
+
if (!state.failingSince)
|
|
782
|
+
return;
|
|
783
|
+
writeStateFile(stateFilePath, { ...state, notifiedFailingSince: state.failingSince });
|
|
784
|
+
}
|
|
785
|
+
function writeStateFile(stateFilePath, state) {
|
|
786
|
+
const temporaryPath = `${stateFilePath}.${process.pid}.tmp`;
|
|
787
|
+
try {
|
|
788
|
+
const dir = path.dirname(stateFilePath);
|
|
789
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
790
|
+
fs.writeFileSync(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
791
|
+
`, { encoding: "utf8", mode: 384 });
|
|
792
|
+
fs.renameSync(temporaryPath, stateFilePath);
|
|
793
|
+
if (process.platform !== "win32")
|
|
794
|
+
fs.chmodSync(stateFilePath, 384);
|
|
795
|
+
} catch {
|
|
796
|
+
try {
|
|
797
|
+
if (fs.existsSync(temporaryPath))
|
|
798
|
+
fs.unlinkSync(temporaryPath);
|
|
799
|
+
} catch {
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
function truncate(value) {
|
|
804
|
+
const collapsed = value.replace(/\s+/g, " ").trim();
|
|
805
|
+
return collapsed.length > 200 ? `${collapsed.slice(0, 197)}...` : collapsed;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
|
|
586
810
|
// ../packages/tool-kit/out/eventSender.js
|
|
587
811
|
var require_eventSender = __commonJS({
|
|
588
812
|
"../packages/tool-kit/out/eventSender.js"(exports) {
|
|
589
813
|
"use strict";
|
|
590
814
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
591
815
|
exports.AscendaEventSender = exports.AscendaSemanticEventError = void 0;
|
|
816
|
+
exports.buildEventPayload = buildEventPayload;
|
|
592
817
|
var tool_contract_1 = require_out();
|
|
818
|
+
var eventLog_1 = require_eventLog();
|
|
593
819
|
var http_1 = require_http();
|
|
594
820
|
var tokenStore_1 = require_tokenStore();
|
|
821
|
+
var stateStore_1 = require_stateStore();
|
|
595
822
|
var AscendaSemanticEventError = class extends Error {
|
|
596
823
|
constructor(message) {
|
|
597
824
|
super(message);
|
|
@@ -599,27 +826,32 @@ var require_eventSender = __commonJS({
|
|
|
599
826
|
}
|
|
600
827
|
};
|
|
601
828
|
exports.AscendaSemanticEventError = AscendaSemanticEventError;
|
|
829
|
+
function buildEventPayload(identity, mapped) {
|
|
830
|
+
return {
|
|
831
|
+
toolInstallationId: identity.toolInstallationId,
|
|
832
|
+
source: identity.source,
|
|
833
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
834
|
+
sessionId: identity.sessionId ?? void 0,
|
|
835
|
+
workspaceHash: identity.workspaceHash ?? void 0,
|
|
836
|
+
consentScope: tool_contract_1.ASCENDA_CONSENT_SCOPE,
|
|
837
|
+
provenance: tool_contract_1.ASCENDA_PROVENANCE,
|
|
838
|
+
privacyMode: "metadata_only",
|
|
839
|
+
...mapped,
|
|
840
|
+
metadata: mapped.metadata ?? {}
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
844
|
+
var RETRY_DELAY_MS = 250;
|
|
602
845
|
var AscendaEventSender2 = class {
|
|
603
846
|
config;
|
|
604
847
|
eventWriteToken;
|
|
848
|
+
lastState;
|
|
605
849
|
constructor(config) {
|
|
606
850
|
this.config = config;
|
|
607
851
|
this.eventWriteToken = config.eventWriteToken;
|
|
608
852
|
}
|
|
609
853
|
async send(mapped) {
|
|
610
|
-
|
|
611
|
-
toolInstallationId: this.config.toolInstallationId,
|
|
612
|
-
source: this.config.source,
|
|
613
|
-
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
614
|
-
sessionId: this.config.sessionId ?? void 0,
|
|
615
|
-
workspaceHash: this.config.workspaceHash ?? void 0,
|
|
616
|
-
consentScope: tool_contract_1.ASCENDA_CONSENT_SCOPE,
|
|
617
|
-
provenance: tool_contract_1.ASCENDA_PROVENANCE,
|
|
618
|
-
privacyMode: "metadata_only",
|
|
619
|
-
...mapped,
|
|
620
|
-
metadata: mapped.metadata ?? {}
|
|
621
|
-
};
|
|
622
|
-
return this.post(payload);
|
|
854
|
+
return this.post(buildEventPayload(this.config, mapped));
|
|
623
855
|
}
|
|
624
856
|
/**
|
|
625
857
|
* Sends one of the six agent-observed types (dark-flow-gap-analysis §2.1).
|
|
@@ -690,32 +922,263 @@ var require_eventSender = __commonJS({
|
|
|
690
922
|
};
|
|
691
923
|
return this.post(payload);
|
|
692
924
|
}
|
|
925
|
+
/**
|
|
926
|
+
* The single choke point every event passes through, and therefore the only
|
|
927
|
+
* honest place to journal one. Recording here rather than in each adapter is
|
|
928
|
+
* deliberate: Claude Code, Codex, the GitHub collector and the MCP server all
|
|
929
|
+
* send through this method, and the defect being fixed showed up in three
|
|
930
|
+
* separate components because each was left to notice its own failures.
|
|
931
|
+
*/
|
|
693
932
|
async post(payload) {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
933
|
+
const outcome = await this.attempt(payload);
|
|
934
|
+
this.lastState = (0, stateStore_1.recordSendOutcome)(this.stateFilePath(), this.config.toolInstallationId, outcome.result, {
|
|
935
|
+
httpStatus: outcome.httpStatus,
|
|
936
|
+
errorCode: outcome.errorCode,
|
|
937
|
+
detail: outcome.detail
|
|
938
|
+
});
|
|
939
|
+
this.log(payload, outcome.result);
|
|
940
|
+
return outcome.result;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Two recoveries, each tried once. A rejected token gets a renewal and one
|
|
944
|
+
* replay — rotation is expected and should heal unattended. A transport
|
|
945
|
+
* error gets one retry, because the common cases (a restarting instance, a
|
|
946
|
+
* proxy blip, a 429) clear in well under a second and the alternative is
|
|
947
|
+
* losing the event outright.
|
|
948
|
+
*/
|
|
949
|
+
async attempt(payload) {
|
|
950
|
+
const outcome = await (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
951
|
+
if (outcome.result === "auth_failed") {
|
|
952
|
+
if (!await this.renewEventToken())
|
|
953
|
+
return outcome;
|
|
954
|
+
return (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
700
955
|
}
|
|
701
|
-
|
|
956
|
+
if (outcome.result === "transport_error" && (outcome.httpStatus === void 0 || (0, http_1.isRetryableStatus)(outcome.httpStatus))) {
|
|
957
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
|
958
|
+
return (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
959
|
+
}
|
|
960
|
+
return outcome;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* The state written by the most recent send, so a caller can decide whether
|
|
964
|
+
* to surface a one-time notice without re-reading the journal it just wrote.
|
|
965
|
+
*/
|
|
966
|
+
get state() {
|
|
967
|
+
return this.lastState;
|
|
968
|
+
}
|
|
969
|
+
stateFilePath() {
|
|
970
|
+
return this.config.stateFilePath ?? (0, stateStore_1.defaultStateFilePath)(this.config.toolInstallationId);
|
|
971
|
+
}
|
|
972
|
+
/** Never throws: a renewal that errors is a failed renewal, not a failed turn. */
|
|
973
|
+
/**
|
|
974
|
+
* Every send path funnels through {@link post}, so semantic and
|
|
975
|
+
* collaboration signals are logged on the same terms as host events — the
|
|
976
|
+
* log would be misleading as an audit of what left the machine otherwise.
|
|
977
|
+
*
|
|
978
|
+
* An unreachable backend used to be logged as `other` from a catch block.
|
|
979
|
+
* It is now `transport_error` through the ordinary path, because the
|
|
980
|
+
* transport returns that outcome instead of throwing.
|
|
981
|
+
*/
|
|
982
|
+
log(payload, delivery) {
|
|
983
|
+
const logFile = this.config.eventLogFile === void 0 ? (0, eventLog_1.resolveEventLogPath)() : this.config.eventLogFile;
|
|
984
|
+
if (!logFile)
|
|
985
|
+
return;
|
|
986
|
+
(0, eventLog_1.appendEventLog)(logFile, { loggedAt: (/* @__PURE__ */ new Date()).toISOString(), delivery, payload });
|
|
702
987
|
}
|
|
703
988
|
async renewEventToken() {
|
|
704
|
-
|
|
705
|
-
|
|
989
|
+
try {
|
|
990
|
+
const renewed = await (0, http_1.renewToolToken)(this.config.apiBaseUrl, this.eventWriteToken, this.signal());
|
|
991
|
+
if (!renewed)
|
|
992
|
+
return false;
|
|
993
|
+
this.eventWriteToken = renewed.eventWriteToken;
|
|
994
|
+
(0, tokenStore_1.persistEventWriteToken)(this.config.tokenFilePath, renewed.eventWriteToken);
|
|
995
|
+
return true;
|
|
996
|
+
} catch {
|
|
706
997
|
return false;
|
|
707
|
-
|
|
708
|
-
(0, tokenStore_1.persistEventWriteToken)(this.config.tokenFilePath, renewed.eventWriteToken);
|
|
709
|
-
return true;
|
|
998
|
+
}
|
|
710
999
|
}
|
|
711
1000
|
signal() {
|
|
712
|
-
return
|
|
1001
|
+
return AbortSignal.timeout(this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
713
1002
|
}
|
|
714
1003
|
};
|
|
715
1004
|
exports.AscendaEventSender = AscendaEventSender2;
|
|
716
1005
|
}
|
|
717
1006
|
});
|
|
718
1007
|
|
|
1008
|
+
// ../packages/tool-kit/out/hookAdapter.js
|
|
1009
|
+
var require_hookAdapter = __commonJS({
|
|
1010
|
+
"../packages/tool-kit/out/hookAdapter.js"(exports) {
|
|
1011
|
+
"use strict";
|
|
1012
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1013
|
+
exports.DEFAULT_API_BASE_URL = void 0;
|
|
1014
|
+
exports.loadCliAgentConfig = loadCliAgentConfig;
|
|
1015
|
+
exports.deliverHookEvents = deliverHookEvents;
|
|
1016
|
+
var eventLog_1 = require_eventLog();
|
|
1017
|
+
var eventSender_1 = require_eventSender();
|
|
1018
|
+
var tokenStore_1 = require_tokenStore();
|
|
1019
|
+
exports.DEFAULT_API_BASE_URL = "https://api.ascenda.one";
|
|
1020
|
+
function loadCliAgentConfig(toolType, sessionIdFromHook) {
|
|
1021
|
+
const apiBaseUrl = (process.env.ASCENDA_API_BASE_URL ?? exports.DEFAULT_API_BASE_URL).replace(/\/$/, "");
|
|
1022
|
+
const toolInstallationIdRaw = process.env.ASCENDA_TOOL_INSTALLATION_ID;
|
|
1023
|
+
if (!toolInstallationIdRaw)
|
|
1024
|
+
throw new Error("Missing ASCENDA_TOOL_INSTALLATION_ID");
|
|
1025
|
+
const toolInstallationId = toolInstallationIdRaw.trim().includes(":") ? toolInstallationIdRaw.trim() : `${toolType}:${toolInstallationIdRaw.trim()}`;
|
|
1026
|
+
const tokenFilePath = process.env.ASCENDA_EVENT_WRITE_TOKEN_FILE ?? (0, tokenStore_1.defaultTokenFilePath)(toolInstallationId);
|
|
1027
|
+
const fileToken = (0, tokenStore_1.readTokenFile)(tokenFilePath);
|
|
1028
|
+
const eventWriteToken = fileToken ?? process.env.ASCENDA_EVENT_WRITE_TOKEN;
|
|
1029
|
+
if (!eventWriteToken)
|
|
1030
|
+
throw new Error("Missing ASCENDA_EVENT_WRITE_TOKEN (or token file)");
|
|
1031
|
+
if (!fileToken)
|
|
1032
|
+
(0, tokenStore_1.persistEventWriteToken)(tokenFilePath, eventWriteToken);
|
|
1033
|
+
return {
|
|
1034
|
+
apiBaseUrl,
|
|
1035
|
+
toolInstallationId,
|
|
1036
|
+
eventWriteToken,
|
|
1037
|
+
tokenFilePath,
|
|
1038
|
+
sessionId: process.env.ASCENDA_SESSION_ID ?? sessionIdFromHook ?? null,
|
|
1039
|
+
workspaceHash: process.env.ASCENDA_WORKSPACE_HASH ?? null,
|
|
1040
|
+
// Agents await command hooks; fail fast rather than stall the user's turn.
|
|
1041
|
+
timeoutMs: parsePositiveInt(process.env.ASCENDA_HTTP_TIMEOUT_MS) ?? 3e3
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
async function deliverHookEvents(events, options) {
|
|
1045
|
+
if (events.length === 0)
|
|
1046
|
+
return;
|
|
1047
|
+
const notice = options.onNotice ?? ((message) => console.error(message));
|
|
1048
|
+
let config;
|
|
1049
|
+
try {
|
|
1050
|
+
config = loadCliAgentConfig(options.toolType, options.sessionId);
|
|
1051
|
+
} catch (error) {
|
|
1052
|
+
const logFile = (0, eventLog_1.resolveEventLogPath)();
|
|
1053
|
+
if (!logFile)
|
|
1054
|
+
throw error;
|
|
1055
|
+
for (const event of events) {
|
|
1056
|
+
(0, eventLog_1.appendEventLog)(logFile, {
|
|
1057
|
+
loggedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1058
|
+
delivery: "not_sent",
|
|
1059
|
+
payload: (0, eventSender_1.buildEventPayload)({
|
|
1060
|
+
toolInstallationId: `${options.toolType}:unpaired`,
|
|
1061
|
+
source: options.source,
|
|
1062
|
+
sessionId: options.sessionId ?? null,
|
|
1063
|
+
workspaceHash: process.env.ASCENDA_WORKSPACE_HASH ?? null
|
|
1064
|
+
}, event)
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
const sender = new eventSender_1.AscendaEventSender({
|
|
1070
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
1071
|
+
toolInstallationId: config.toolInstallationId,
|
|
1072
|
+
source: options.source,
|
|
1073
|
+
eventWriteToken: config.eventWriteToken,
|
|
1074
|
+
tokenFilePath: config.tokenFilePath,
|
|
1075
|
+
sessionId: config.sessionId,
|
|
1076
|
+
workspaceHash: config.workspaceHash,
|
|
1077
|
+
timeoutMs: config.timeoutMs
|
|
1078
|
+
});
|
|
1079
|
+
for (const event of events) {
|
|
1080
|
+
const result = await sender.send(event);
|
|
1081
|
+
if (result === "accepted")
|
|
1082
|
+
continue;
|
|
1083
|
+
if (result === "consent_missing") {
|
|
1084
|
+
notice("Ascenda telemetry paused: renew IDE telemetry consent in the Ascenda app.");
|
|
1085
|
+
} else if (result === "auth_failed") {
|
|
1086
|
+
notice("Ascenda telemetry paused: connection revoked or expired. Re-pair via an Ascenda IDE extension or pairing-sim.");
|
|
1087
|
+
} else if (result === "transport_error") {
|
|
1088
|
+
notice("Ascenda telemetry paused: the ingest endpoint could not be reached. Your work is unaffected.");
|
|
1089
|
+
} else {
|
|
1090
|
+
notice(`Ascenda telemetry rejected: ${result}`);
|
|
1091
|
+
}
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
function parsePositiveInt(value) {
|
|
1096
|
+
const n = Number(value);
|
|
1097
|
+
return Number.isInteger(n) && n > 0 ? n : void 0;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
// ../packages/tool-kit/out/turnState.js
|
|
1103
|
+
var require_turnState = __commonJS({
|
|
1104
|
+
"../packages/tool-kit/out/turnState.js"(exports) {
|
|
1105
|
+
"use strict";
|
|
1106
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1107
|
+
if (k2 === void 0) k2 = k;
|
|
1108
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1109
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1110
|
+
desc = { enumerable: true, get: function() {
|
|
1111
|
+
return m[k];
|
|
1112
|
+
} };
|
|
1113
|
+
}
|
|
1114
|
+
Object.defineProperty(o, k2, desc);
|
|
1115
|
+
} : function(o, m, k, k2) {
|
|
1116
|
+
if (k2 === void 0) k2 = k;
|
|
1117
|
+
o[k2] = m[k];
|
|
1118
|
+
});
|
|
1119
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
1120
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1121
|
+
} : function(o, v) {
|
|
1122
|
+
o["default"] = v;
|
|
1123
|
+
});
|
|
1124
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
1125
|
+
var ownKeys = function(o) {
|
|
1126
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1127
|
+
var ar = [];
|
|
1128
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1129
|
+
return ar;
|
|
1130
|
+
};
|
|
1131
|
+
return ownKeys(o);
|
|
1132
|
+
};
|
|
1133
|
+
return function(mod) {
|
|
1134
|
+
if (mod && mod.__esModule) return mod;
|
|
1135
|
+
var result = {};
|
|
1136
|
+
if (mod != null) {
|
|
1137
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1138
|
+
}
|
|
1139
|
+
__setModuleDefault(result, mod);
|
|
1140
|
+
return result;
|
|
1141
|
+
};
|
|
1142
|
+
}();
|
|
1143
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1144
|
+
exports.recordTurnStart = recordTurnStart;
|
|
1145
|
+
exports.consumeTurnDurationMs = consumeTurnDurationMs;
|
|
1146
|
+
var fs = __importStar(__require("fs"));
|
|
1147
|
+
var os = __importStar(__require("os"));
|
|
1148
|
+
var path = __importStar(__require("path"));
|
|
1149
|
+
var stateDir = () => process.env.ASCENDA_STATE_DIR ?? path.join(os.homedir(), ".ascenda", "state");
|
|
1150
|
+
function turnFile(agent, sessionId) {
|
|
1151
|
+
return path.join(stateDir(), `${sanitize(agent)}-turn-${sanitize(sessionId)}`);
|
|
1152
|
+
}
|
|
1153
|
+
function sanitize(value) {
|
|
1154
|
+
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
1155
|
+
}
|
|
1156
|
+
function recordTurnStart(agent, sessionId, now = Date.now()) {
|
|
1157
|
+
if (!sessionId)
|
|
1158
|
+
return;
|
|
1159
|
+
try {
|
|
1160
|
+
fs.mkdirSync(stateDir(), { recursive: true });
|
|
1161
|
+
fs.writeFileSync(turnFile(agent, sessionId), String(now), { encoding: "utf8", mode: 384 });
|
|
1162
|
+
} catch {
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
function consumeTurnDurationMs(agent, sessionId, now = Date.now()) {
|
|
1166
|
+
if (!sessionId)
|
|
1167
|
+
return void 0;
|
|
1168
|
+
const file = turnFile(agent, sessionId);
|
|
1169
|
+
try {
|
|
1170
|
+
const started = Number(fs.readFileSync(file, "utf8").trim());
|
|
1171
|
+
fs.rmSync(file, { force: true });
|
|
1172
|
+
if (!Number.isFinite(started) || started <= 0 || started > now)
|
|
1173
|
+
return void 0;
|
|
1174
|
+
return now - started;
|
|
1175
|
+
} catch {
|
|
1176
|
+
return void 0;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
|
|
719
1182
|
// ../packages/tool-kit/out/salt.js
|
|
720
1183
|
var require_salt = __commonJS({
|
|
721
1184
|
"../packages/tool-kit/out/salt.js"(exports) {
|
|
@@ -930,7 +1393,8 @@ var require_out2 = __commonJS({
|
|
|
930
1393
|
"../packages/tool-kit/out/index.js"(exports) {
|
|
931
1394
|
"use strict";
|
|
932
1395
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
933
|
-
exports.
|
|
1396
|
+
exports.postToolEventsBatch = exports.postToolEvent = exports.renewToolToken = exports.getPairingStatus = exports.createPairingSession = exports.AscendaApiError = exports.liveBusSocketCandidates = exports.liveBusSocketPath = exports.bucketPromptSize = exports.emitLiveSignal = 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.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.isAfterHours = exports.bucketDurationMs = exports.bucketLinesChanged = exports.invitesDebrief = exports.classifyWorkMilestone = exports.isReworkGitAction = exports.classifyGitAction = exports.isVerificationCommand = exports.classifyCommand = void 0;
|
|
1397
|
+
exports.isRetryableStatus = exports.parseIngestResponse = void 0;
|
|
934
1398
|
var commandClassifier_1 = require_commandClassifier();
|
|
935
1399
|
Object.defineProperty(exports, "classifyCommand", { enumerable: true, get: function() {
|
|
936
1400
|
return commandClassifier_1.classifyCommand;
|
|
@@ -995,6 +1459,39 @@ var require_out2 = __commonJS({
|
|
|
995
1459
|
Object.defineProperty(exports, "AscendaSemanticEventError", { enumerable: true, get: function() {
|
|
996
1460
|
return eventSender_1.AscendaSemanticEventError;
|
|
997
1461
|
} });
|
|
1462
|
+
Object.defineProperty(exports, "buildEventPayload", { enumerable: true, get: function() {
|
|
1463
|
+
return eventSender_1.buildEventPayload;
|
|
1464
|
+
} });
|
|
1465
|
+
var eventLog_1 = require_eventLog();
|
|
1466
|
+
Object.defineProperty(exports, "EVENT_LOG_ENV_VAR", { enumerable: true, get: function() {
|
|
1467
|
+
return eventLog_1.EVENT_LOG_ENV_VAR;
|
|
1468
|
+
} });
|
|
1469
|
+
Object.defineProperty(exports, "appendEventLog", { enumerable: true, get: function() {
|
|
1470
|
+
return eventLog_1.appendEventLog;
|
|
1471
|
+
} });
|
|
1472
|
+
Object.defineProperty(exports, "expandUserPath", { enumerable: true, get: function() {
|
|
1473
|
+
return eventLog_1.expandUserPath;
|
|
1474
|
+
} });
|
|
1475
|
+
Object.defineProperty(exports, "resolveEventLogPath", { enumerable: true, get: function() {
|
|
1476
|
+
return eventLog_1.resolveEventLogPath;
|
|
1477
|
+
} });
|
|
1478
|
+
var hookAdapter_1 = require_hookAdapter();
|
|
1479
|
+
Object.defineProperty(exports, "DEFAULT_API_BASE_URL", { enumerable: true, get: function() {
|
|
1480
|
+
return hookAdapter_1.DEFAULT_API_BASE_URL;
|
|
1481
|
+
} });
|
|
1482
|
+
Object.defineProperty(exports, "deliverHookEvents", { enumerable: true, get: function() {
|
|
1483
|
+
return hookAdapter_1.deliverHookEvents;
|
|
1484
|
+
} });
|
|
1485
|
+
Object.defineProperty(exports, "loadCliAgentConfig", { enumerable: true, get: function() {
|
|
1486
|
+
return hookAdapter_1.loadCliAgentConfig;
|
|
1487
|
+
} });
|
|
1488
|
+
var turnState_1 = require_turnState();
|
|
1489
|
+
Object.defineProperty(exports, "consumeTurnDurationMs", { enumerable: true, get: function() {
|
|
1490
|
+
return turnState_1.consumeTurnDurationMs;
|
|
1491
|
+
} });
|
|
1492
|
+
Object.defineProperty(exports, "recordTurnStart", { enumerable: true, get: function() {
|
|
1493
|
+
return turnState_1.recordTurnStart;
|
|
1494
|
+
} });
|
|
998
1495
|
var tokenStore_1 = require_tokenStore();
|
|
999
1496
|
Object.defineProperty(exports, "defaultTokenFilePath", { enumerable: true, get: function() {
|
|
1000
1497
|
return tokenStore_1.defaultTokenFilePath;
|
|
@@ -1005,6 +1502,22 @@ var require_out2 = __commonJS({
|
|
|
1005
1502
|
Object.defineProperty(exports, "readTokenFile", { enumerable: true, get: function() {
|
|
1006
1503
|
return tokenStore_1.readTokenFile;
|
|
1007
1504
|
} });
|
|
1505
|
+
var stateStore_1 = require_stateStore();
|
|
1506
|
+
Object.defineProperty(exports, "defaultStateFilePath", { enumerable: true, get: function() {
|
|
1507
|
+
return stateStore_1.defaultStateFilePath;
|
|
1508
|
+
} });
|
|
1509
|
+
Object.defineProperty(exports, "readCollectorState", { enumerable: true, get: function() {
|
|
1510
|
+
return stateStore_1.readCollectorState;
|
|
1511
|
+
} });
|
|
1512
|
+
Object.defineProperty(exports, "recordSendOutcome", { enumerable: true, get: function() {
|
|
1513
|
+
return stateStore_1.recordSendOutcome;
|
|
1514
|
+
} });
|
|
1515
|
+
Object.defineProperty(exports, "shouldAnnounceFailure", { enumerable: true, get: function() {
|
|
1516
|
+
return stateStore_1.shouldAnnounceFailure;
|
|
1517
|
+
} });
|
|
1518
|
+
Object.defineProperty(exports, "markFailureNotified", { enumerable: true, get: function() {
|
|
1519
|
+
return stateStore_1.markFailureNotified;
|
|
1520
|
+
} });
|
|
1008
1521
|
var salt_1 = require_salt();
|
|
1009
1522
|
Object.defineProperty(exports, "machineSaltFilePath", { enumerable: true, get: function() {
|
|
1010
1523
|
return salt_1.machineSaltFilePath;
|
|
@@ -1050,6 +1563,9 @@ var require_out2 = __commonJS({
|
|
|
1050
1563
|
Object.defineProperty(exports, "parseIngestResponse", { enumerable: true, get: function() {
|
|
1051
1564
|
return http_1.parseIngestResponse;
|
|
1052
1565
|
} });
|
|
1566
|
+
Object.defineProperty(exports, "isRetryableStatus", { enumerable: true, get: function() {
|
|
1567
|
+
return http_1.isRetryableStatus;
|
|
1568
|
+
} });
|
|
1053
1569
|
}
|
|
1054
1570
|
});
|
|
1055
1571
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"publishConfig": { "access": "public" },
|
|
3
3
|
"name": "@ascenda-one/github-collector",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.9",
|
|
5
5
|
"description": "Code-forge collaboration collector for Ascenda work telemetry — first-person review and pull-request signals.",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|