@ascenda-one/github-collector 0.1.14 → 0.1.16
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 +1843 -130
- package/dist/mapForgeEvent.js +10 -16
- package/dist/mapForgeEvent.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -200,6 +200,102 @@ var require_workMilestoneClassifier = __commonJS({
|
|
|
200
200
|
}
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
+
// ../packages/tool-kit/out/autonomyBand.js
|
|
204
|
+
var require_autonomyBand = __commonJS({
|
|
205
|
+
"../packages/tool-kit/out/autonomyBand.js"(exports) {
|
|
206
|
+
"use strict";
|
|
207
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
208
|
+
exports.autonomyBand = autonomyBand;
|
|
209
|
+
function autonomyBand(mode) {
|
|
210
|
+
if (typeof mode !== "string")
|
|
211
|
+
return "unknown";
|
|
212
|
+
return BAND_BY_MODE[mode] ?? "unknown";
|
|
213
|
+
}
|
|
214
|
+
var BAND_BY_MODE = {
|
|
215
|
+
plan: "planning",
|
|
216
|
+
default: "supervised",
|
|
217
|
+
accept_edits: "edits_auto",
|
|
218
|
+
// Two tokens, one band — and the reason the tokens stayed two. They differ
|
|
219
|
+
// in how the user arrived at the posture rather than in how much the agent
|
|
220
|
+
// may then do unasked, so today they read the same. If that ever stops being
|
|
221
|
+
// true, this line changes and the whole corpus re-reads correctly, because
|
|
222
|
+
// the wire never collapsed them.
|
|
223
|
+
auto: "delegated",
|
|
224
|
+
dont_ask: "delegated",
|
|
225
|
+
bypass_permissions: "unsupervised"
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// ../packages/tool-kit/out/modelClassifier.js
|
|
231
|
+
var require_modelClassifier = __commonJS({
|
|
232
|
+
"../packages/tool-kit/out/modelClassifier.js"(exports) {
|
|
233
|
+
"use strict";
|
|
234
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
235
|
+
exports.classifyModelClass = classifyModelClass;
|
|
236
|
+
function classifyModelClass(raw) {
|
|
237
|
+
const candidate = raw;
|
|
238
|
+
if (candidate === void 0 || candidate === null)
|
|
239
|
+
return void 0;
|
|
240
|
+
if (typeof candidate !== "string")
|
|
241
|
+
return "unknown";
|
|
242
|
+
const value = candidate.trim().toLowerCase();
|
|
243
|
+
if (!value)
|
|
244
|
+
return void 0;
|
|
245
|
+
if (ROUTER_SENTINEL.test(value))
|
|
246
|
+
return "router:auto";
|
|
247
|
+
const vendor = readModelVendor(value);
|
|
248
|
+
if (vendor === void 0)
|
|
249
|
+
return "unknown";
|
|
250
|
+
for (const [pattern, modelClass] of TIER_PATTERNS_BY_VENDOR[vendor]) {
|
|
251
|
+
if (pattern.test(value))
|
|
252
|
+
return modelClass;
|
|
253
|
+
}
|
|
254
|
+
return UNKNOWN_TIER_BY_VENDOR[vendor];
|
|
255
|
+
}
|
|
256
|
+
function readModelVendor(value) {
|
|
257
|
+
for (const [pattern, vendor] of VENDOR_PATTERNS) {
|
|
258
|
+
if (pattern.test(value))
|
|
259
|
+
return vendor;
|
|
260
|
+
}
|
|
261
|
+
return void 0;
|
|
262
|
+
}
|
|
263
|
+
var ROUTER_SENTINEL = /^(?:[a-z0-9][a-z0-9._-]*\/)?(?:auto|default)$/;
|
|
264
|
+
var VENDOR_PATTERNS = [
|
|
265
|
+
[/\b(anthropic|claude|opus|sonnet|haiku|fable)\b/, "anthropic"],
|
|
266
|
+
[/\b(openai|gpt|o[1-9])\b/, "openai"],
|
|
267
|
+
[/\b(google|gemini|vertex)\b/, "google"],
|
|
268
|
+
// xAI carries no corporate prefix in any observed id — the family name is
|
|
269
|
+
// the whole marker, exactly as `claude` and `gemini` are for theirs.
|
|
270
|
+
[/\b(xai|grok)\b/, "xai"],
|
|
271
|
+
[/\b(ollama|llamacpp|on[-_]?device|local)\b/, "local"]
|
|
272
|
+
];
|
|
273
|
+
var TIER_PATTERNS_BY_VENDOR = {
|
|
274
|
+
anthropic: [
|
|
275
|
+
[/\bopus\b/, "anthropic:opus"],
|
|
276
|
+
[/\bsonnet\b/, "anthropic:sonnet"],
|
|
277
|
+
[/\bhaiku\b/, "anthropic:haiku"],
|
|
278
|
+
[/\bfable\b/, "anthropic:fable"]
|
|
279
|
+
],
|
|
280
|
+
openai: [[/\bgpt\b/, "openai:gpt"]],
|
|
281
|
+
google: [[/\bgemini\b/, "google:gemini"]],
|
|
282
|
+
// One tier for now. The line's coding variants (`grok-code-fast-1`) are the
|
|
283
|
+
// same tier word plus a suffix, and splitting them off would be inventing a
|
|
284
|
+
// distinction the ids do not yet draw — `<vendor>:unknown` is waiting for
|
|
285
|
+
// the day one does.
|
|
286
|
+
xai: [[/\bgrok\b/, "xai:grok"]],
|
|
287
|
+
local: [[/\b(ollama|llamacpp|on[-_]?device)\b/, "local:on_device"]]
|
|
288
|
+
};
|
|
289
|
+
var UNKNOWN_TIER_BY_VENDOR = {
|
|
290
|
+
anthropic: "anthropic:unknown",
|
|
291
|
+
openai: "openai:unknown",
|
|
292
|
+
google: "google:unknown",
|
|
293
|
+
xai: "xai:unknown",
|
|
294
|
+
local: "local:unknown"
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
203
299
|
// ../packages/tool-kit/out/buckets.js
|
|
204
300
|
var require_buckets = __commonJS({
|
|
205
301
|
"../packages/tool-kit/out/buckets.js"(exports) {
|
|
@@ -294,11 +390,298 @@ var require_afterHours = __commonJS({
|
|
|
294
390
|
}
|
|
295
391
|
});
|
|
296
392
|
|
|
393
|
+
// ../packages/tool-contract/out/metricKeys.js
|
|
394
|
+
var require_metricKeys = __commonJS({
|
|
395
|
+
"../packages/tool-contract/out/metricKeys.js"(exports) {
|
|
396
|
+
"use strict";
|
|
397
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
398
|
+
exports.METRIC_KEYS = void 0;
|
|
399
|
+
exports.backendMetricKeys = backendMetricKeys;
|
|
400
|
+
var CONTEXT_WINDOW_CANONICAL_ALIASES = [
|
|
401
|
+
"contextWindowPeakPct",
|
|
402
|
+
"context_window_peak_pct",
|
|
403
|
+
"contextWindowPct",
|
|
404
|
+
"context_window_pct"
|
|
405
|
+
];
|
|
406
|
+
var CONTEXT_WINDOW_CURSOR_PERCENT_ALIASES = ["contextUsagePercent"];
|
|
407
|
+
exports.METRIC_KEYS = {
|
|
408
|
+
// ── Read by a backend reader ────────────────────────────────────────────
|
|
409
|
+
contextWindowPeakPct: {
|
|
410
|
+
readBy: ["backend", "handoff"],
|
|
411
|
+
backendAliases: CONTEXT_WINDOW_CANONICAL_ALIASES,
|
|
412
|
+
unit: "fraction of the context window (0\u20131; uncapped for >200k contexts)",
|
|
413
|
+
note: "Claude Code reports a true per-session peak. Cursor reports its composer's last known occupancy under the same key \u2014 the closest its store can answer, and not the same measurement."
|
|
414
|
+
},
|
|
415
|
+
contextUsagePercent: {
|
|
416
|
+
readBy: ["backend", "handoff"],
|
|
417
|
+
backendAliases: CONTEXT_WINDOW_CURSOR_PERCENT_ALIASES,
|
|
418
|
+
unit: "percent (0\u2013100)",
|
|
419
|
+
note: "Cursor's own column name. Superseded by contextWindowPeakPct on the wire; kept because the handoff reads it and imported rows carry it. Unit-explicit on the backend: always divided by 100, never put through the fraction-or-percent heuristic."
|
|
420
|
+
},
|
|
421
|
+
promptCount: { readBy: ["backend", "handoff"], backendAliases: ["promptCount", "prompt_count"] },
|
|
422
|
+
sessionMinutes: { readBy: ["backend"], backendAliases: ["sessionMinutes", "session_minutes"], unit: "minutes" },
|
|
423
|
+
durationBucket: { readBy: ["backend", "handoff"], backendAliases: ["durationBucket", "duration_bucket"] },
|
|
424
|
+
afterHoursPrompts: { readBy: ["backend", "handoff"], backendAliases: ["afterHoursPrompts", "after_hours_prompts"] },
|
|
425
|
+
inputTokens: { readBy: ["backend"], backendAliases: ["inputTokens", "input_tokens"], unit: "tokens" },
|
|
426
|
+
outputTokens: { readBy: ["backend"], backendAliases: ["outputTokens", "output_tokens"], unit: "tokens" },
|
|
427
|
+
cacheReadTokens: { readBy: ["backend"], backendAliases: ["cacheReadTokens", "cache_read_tokens"], unit: "tokens" },
|
|
428
|
+
queuedPrompts: { readBy: ["backend"], backendAliases: ["queuedPrompts", "queued_prompts"] },
|
|
429
|
+
linesChangedBucket: { readBy: ["backend"], backendAliases: ["linesChangedBucket", "lines_changed_bucket"] },
|
|
430
|
+
// ── Read by the local handoff only ──────────────────────────────────────
|
|
431
|
+
activeMinutes: { readBy: ["handoff"], unit: "minutes" },
|
|
432
|
+
/**
|
|
433
|
+
* The two halves of `activeMinutes`, and deliberately two keys.
|
|
434
|
+
*
|
|
435
|
+
* They partition it exactly, so a reader can add them — but there is no
|
|
436
|
+
* third key holding the sum, because the sum is `activeMinutes` and it
|
|
437
|
+
* already exists. Presenting one combined "active" figure in place of these
|
|
438
|
+
* is the thing the split was added to stop: an hour of typing and an hour of
|
|
439
|
+
* watching an agent work are not the same hour, and a single number says
|
|
440
|
+
* they are.
|
|
441
|
+
*/
|
|
442
|
+
handsOnMinutes: {
|
|
443
|
+
readBy: ["handoff"],
|
|
444
|
+
unit: "minutes",
|
|
445
|
+
note: "Active time immediately preceding a human prompt \u2014 the only interval a transcript can show a person present for, because the prompt at its end is the evidence."
|
|
446
|
+
},
|
|
447
|
+
agentSupervisingMinutes: {
|
|
448
|
+
readBy: ["handoff"],
|
|
449
|
+
unit: "minutes",
|
|
450
|
+
note: "The remaining active time: the agent was working and the person was not typing. NOT a claim that anyone watched it \u2014 nothing in a transcript could show that. Never render as attention."
|
|
451
|
+
},
|
|
452
|
+
// The split's honesty counters. Read by neither the backend nor the handoff
|
|
453
|
+
// on purpose: they exist so a thin or posture-blind session can be told from
|
|
454
|
+
// a complete one, and a reader that ignores them is choosing to, rather than
|
|
455
|
+
// being unable to.
|
|
456
|
+
activeSplitInstants: {
|
|
457
|
+
readBy: ["diagnostic"],
|
|
458
|
+
note: "Distinct timestamps the split ran over, after collapsing ties. The denominator: two minutes off four instants and off four hundred are not the same measurement."
|
|
459
|
+
},
|
|
460
|
+
activeSplitUndatedLines: {
|
|
461
|
+
readBy: ["diagnostic"],
|
|
462
|
+
note: "Known lines carrying a timestamp that would not parse. Absent from the timeline, so both halves are short by an unknown amount and only this says so."
|
|
463
|
+
},
|
|
464
|
+
activeSplitUnposturedInstants: {
|
|
465
|
+
readBy: ["diagnostic"],
|
|
466
|
+
note: "Instants reached before any permissionMode had been declared. Their supervising time lands in the unknown band, which is a blind spot rather than a posture."
|
|
467
|
+
},
|
|
468
|
+
afterHoursRequests: { readBy: ["handoff"] },
|
|
469
|
+
approximateLintErrorsCount: { readBy: ["handoff"] },
|
|
470
|
+
canceledCount: { readBy: ["handoff"] },
|
|
471
|
+
chatEditCount: { readBy: ["handoff"] },
|
|
472
|
+
compactionCount: {
|
|
473
|
+
readBy: ["handoff"],
|
|
474
|
+
note: "The backend counts context_compression_* event rows, not this key. Both are emitted; this one is for the handoff."
|
|
475
|
+
},
|
|
476
|
+
contextWindowPeakTokens: {
|
|
477
|
+
readBy: ["handoff"],
|
|
478
|
+
unit: "tokens",
|
|
479
|
+
note: "The measured quantity, with no assumed denominator. Prefer this to the ratio for any within-person baseline."
|
|
480
|
+
},
|
|
481
|
+
date: { readBy: ["handoff"] },
|
|
482
|
+
errorCount: { readBy: ["handoff"] },
|
|
483
|
+
filesChangedCount: { readBy: ["handoff"] },
|
|
484
|
+
humanChangesCount: { readBy: ["handoff"] },
|
|
485
|
+
linesAdded: { readBy: ["handoff"] },
|
|
486
|
+
linesRemoved: { readBy: ["handoff"] },
|
|
487
|
+
primaryModel: { readBy: ["handoff"] },
|
|
488
|
+
requestCount: { readBy: ["handoff"] },
|
|
489
|
+
sessionStartedAt: { readBy: ["handoff"] },
|
|
490
|
+
subagentComposers: { readBy: ["handoff"] },
|
|
491
|
+
subagentToolCallCount: {
|
|
492
|
+
readBy: ["handoff"],
|
|
493
|
+
note: "Claude Code only: calls made inside subagent transcripts, kept out of toolCallCount so the main-loop count matches what the wire events carry."
|
|
494
|
+
},
|
|
495
|
+
subagentTranscripts: { readBy: ["handoff"] },
|
|
496
|
+
toolCallCount: {
|
|
497
|
+
readBy: ["handoff"],
|
|
498
|
+
note: "The backend counts ai_tool_call_started event rows, not this key \u2014 same split as compactionCount. Deduplicated on the tool-call id; see each extractor for what one call means in its store."
|
|
499
|
+
},
|
|
500
|
+
toolCallsUndated: {
|
|
501
|
+
readBy: ["handoff"],
|
|
502
|
+
note: "Cursor only: calls whose every record carries an empty createdAt. Counted in toolCallCount, but no event exists for them \u2014 the wire total is smaller than this session count by exactly this number."
|
|
503
|
+
},
|
|
504
|
+
toolFailureCount: { readBy: ["handoff"] },
|
|
505
|
+
totalEntryCount: { readBy: ["handoff"] },
|
|
506
|
+
userModifiedEditCount: {
|
|
507
|
+
readBy: ["handoff"],
|
|
508
|
+
note: "Null, never 0 \u2014 Claude Code never sets userModified true, so 0 would assert 'no AI edit was ever corrected by hand'."
|
|
509
|
+
},
|
|
510
|
+
// ── Diagnostic: read by nobody, on purpose ──────────────────────────────
|
|
511
|
+
abandonedPromptCount: { readBy: ["diagnostic"] },
|
|
512
|
+
// Epoch-marker metrics. The marker is local-only and never reaches the wire
|
|
513
|
+
// (see EXTRACTION_EPOCH_KIND), but it travels as a NormalizedHistoricalEvent
|
|
514
|
+
// and so is keyed by the same vocabulary.
|
|
515
|
+
windowOldest: { readBy: ["handoff"], note: "Oldest event the extraction saw \u2014 the marker's left edge." },
|
|
516
|
+
windowNewest: { readBy: ["handoff"], note: "Newest event the extraction saw \u2014 the marker's right edge." },
|
|
517
|
+
projectsWithNoReadableTranscript: { readBy: ["diagnostic"] },
|
|
518
|
+
unparsedComposerHeaders: { readBy: ["diagnostic"] },
|
|
519
|
+
unknownComposerHeaderTypes: { readBy: ["diagnostic"] },
|
|
520
|
+
orphanedBubbles: { readBy: ["diagnostic"] },
|
|
521
|
+
orphanedSubagentBubbles: { readBy: ["diagnostic"] },
|
|
522
|
+
sessionsWithoutTimeline: { readBy: ["diagnostic"] },
|
|
523
|
+
emptyComposers: { readBy: ["diagnostic"] },
|
|
524
|
+
apiErrorCount: { readBy: ["diagnostic"] },
|
|
525
|
+
assistantTurns: { readBy: ["diagnostic"] },
|
|
526
|
+
compactionAutoCount: { readBy: ["diagnostic"] },
|
|
527
|
+
compactionManualCount: { readBy: ["diagnostic"] },
|
|
528
|
+
editDayCount: { readBy: ["diagnostic"] },
|
|
529
|
+
emptyChatSessions: { readBy: ["diagnostic"] },
|
|
530
|
+
gitBranch: { readBy: ["diagnostic"] },
|
|
531
|
+
linesChanged: { readBy: ["diagnostic"] },
|
|
532
|
+
malformedChatSessionLines: { readBy: ["diagnostic"] },
|
|
533
|
+
malformedHistoryEntries: { readBy: ["diagnostic"] },
|
|
534
|
+
mode: { readBy: ["diagnostic"] },
|
|
535
|
+
modelCount: { readBy: ["diagnostic"] },
|
|
536
|
+
modelSwitchCount: { readBy: ["diagnostic"] },
|
|
537
|
+
rapidRepromptCount: { readBy: ["diagnostic"] },
|
|
538
|
+
schemaUnreadable: { readBy: ["diagnostic"] },
|
|
539
|
+
sessionCount: { readBy: ["diagnostic"] },
|
|
540
|
+
sessionsFromBubbleTimeline: { readBy: ["diagnostic"] },
|
|
541
|
+
sessionsFromCheckpointTimeline: { readBy: ["diagnostic"] },
|
|
542
|
+
sessionsFromRecencyTimeline: { readBy: ["diagnostic"] },
|
|
543
|
+
subagentAssistantTurns: { readBy: ["diagnostic"] },
|
|
544
|
+
subagentPrompts: { readBy: ["diagnostic"] },
|
|
545
|
+
subagentTokensTotal: { readBy: ["diagnostic"] },
|
|
546
|
+
toolName: {
|
|
547
|
+
readBy: ["diagnostic"],
|
|
548
|
+
note: "Set per ai_tool_call_started event by #43's extractors and shipped in wire metadata, but no reader resolves it server-side yet (the backend's ToolName column is MCP audit, not telemetry). Registered after the fact: #38 and #43 merged past each other, and the union caught it on the next compile \u2014 metaLines all over again."
|
|
549
|
+
},
|
|
550
|
+
toolResultCount: { readBy: ["diagnostic"] },
|
|
551
|
+
toolResultErrorCount: { readBy: ["diagnostic"] },
|
|
552
|
+
unknownBubbles: { readBy: ["diagnostic"] },
|
|
553
|
+
unknownLines: { readBy: ["diagnostic"] },
|
|
554
|
+
metaLines: {
|
|
555
|
+
readBy: ["diagnostic"],
|
|
556
|
+
note: 'Recognised-but-skipped transcript machinery (file-history-snapshot, queued-command, \u2026). Split out of unknownLines by #43 so that number keeps meaning "a type nobody has looked at". Registered here after the fact: #41 and #43 merged past each other, and the union caught it on the next compile \u2014 which is this module doing its job.'
|
|
557
|
+
},
|
|
558
|
+
unparsedBubbles: { readBy: ["diagnostic"] },
|
|
559
|
+
unparsedChatSessionFiles: { readBy: ["diagnostic"] },
|
|
560
|
+
unparsedHistoryFiles: { readBy: ["diagnostic"] },
|
|
561
|
+
unparsedLines: { readBy: ["diagnostic"] },
|
|
562
|
+
unreadableChatSessionFiles: { readBy: ["diagnostic"] },
|
|
563
|
+
unreadableHistoryFiles: { readBy: ["diagnostic"] },
|
|
564
|
+
unrecognisedChatSessionFiles: { readBy: ["diagnostic"] }
|
|
565
|
+
};
|
|
566
|
+
function backendMetricKeys() {
|
|
567
|
+
return Object.entries(exports.METRIC_KEYS).filter(([, spec]) => spec.readBy.includes("backend")).map(([key, spec]) => [key, spec.backendAliases ?? [key]]);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
// ../packages/tool-contract/out/index.js
|
|
573
|
+
var require_out = __commonJS({
|
|
574
|
+
"../packages/tool-contract/out/index.js"(exports) {
|
|
575
|
+
"use strict";
|
|
576
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
577
|
+
exports.backendMetricKeys = exports.METRIC_KEYS = exports.ASCENDA_SEMANTIC_PROVENANCE = exports.ASCENDA_HISTORICAL_CONSENT_SCOPE = exports.ASCENDA_COLLABORATION_CONSENT_SCOPE = exports.ASCENDA_SEMANTIC_CONSENT_SCOPE = exports.ASCENDA_PROVENANCE = exports.ASCENDA_CONSENT_SCOPE = exports.EVENT_WORKLOAD_CATEGORY = exports.TOOL_EVENT_DELIVERED_STATUSES = exports.IDEMPOTENCY_KEY_MAX_LENGTH = exports.EVENT_METADATA_FIELDS = exports.COLLABORATION_EVENT_TYPES = exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = void 0;
|
|
578
|
+
exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = [
|
|
579
|
+
"approach_churn_detected",
|
|
580
|
+
"goal_drift_detected",
|
|
581
|
+
"progress_stalled",
|
|
582
|
+
"progress_recovered",
|
|
583
|
+
"session_intention_declared",
|
|
584
|
+
"scope_change_declared"
|
|
585
|
+
];
|
|
586
|
+
exports.COLLABORATION_EVENT_TYPES = [
|
|
587
|
+
"review_requested_of_me",
|
|
588
|
+
"review_given",
|
|
589
|
+
"pull_request_opened"
|
|
590
|
+
];
|
|
591
|
+
exports.EVENT_METADATA_FIELDS = [
|
|
592
|
+
"language",
|
|
593
|
+
"fileType",
|
|
594
|
+
"durationBucket",
|
|
595
|
+
"tokenPressureBucket",
|
|
596
|
+
"linesChangedBucket",
|
|
597
|
+
"commandClass",
|
|
598
|
+
"gitAction",
|
|
599
|
+
"milestoneKind",
|
|
600
|
+
"branchHash",
|
|
601
|
+
"autonomyMode",
|
|
602
|
+
"modelClass",
|
|
603
|
+
"modelId",
|
|
604
|
+
"userModified",
|
|
605
|
+
"outcome",
|
|
606
|
+
"trigger",
|
|
607
|
+
"promptClass",
|
|
608
|
+
"reason",
|
|
609
|
+
"afterHours",
|
|
610
|
+
"activity",
|
|
611
|
+
"message",
|
|
612
|
+
"host",
|
|
613
|
+
"toolName",
|
|
614
|
+
"simulated",
|
|
615
|
+
"relatedEventType",
|
|
616
|
+
"skillVersion",
|
|
617
|
+
"taskFingerprint",
|
|
618
|
+
"importKey",
|
|
619
|
+
"extractionId",
|
|
620
|
+
"importSchema"
|
|
621
|
+
];
|
|
622
|
+
exports.IDEMPOTENCY_KEY_MAX_LENGTH = 128;
|
|
623
|
+
exports.TOOL_EVENT_DELIVERED_STATUSES = ["accepted", "duplicate"];
|
|
624
|
+
exports.EVENT_WORKLOAD_CATEGORY = {
|
|
625
|
+
create_focus_session: "creation",
|
|
626
|
+
ai_prompt_submitted: "creation",
|
|
627
|
+
ai_generation_completed: "creation",
|
|
628
|
+
ai_file_write: "creation",
|
|
629
|
+
ai_file_edit: "creation",
|
|
630
|
+
editor_verification_activity: "verification",
|
|
631
|
+
compile_diagnostic: "verification",
|
|
632
|
+
editor_correction_activity: "supervision",
|
|
633
|
+
ai_correction_prompt: "supervision",
|
|
634
|
+
supervis_meeting_load: "supervision",
|
|
635
|
+
ai_tool_call_started: "supervision",
|
|
636
|
+
ai_tool_call_completed: "supervision",
|
|
637
|
+
ai_tool_call_failed: "supervision",
|
|
638
|
+
// Collaboration (the report's §4.2 collaboration family). Both review
|
|
639
|
+
// events are supervision: being asked to check work, and checking it, are
|
|
640
|
+
// the load the report's "verification overload" concern is about — the one
|
|
641
|
+
// that concentrates on senior engineers as a team adopts AI. Opening a pull
|
|
642
|
+
// request is creation: it is the point your own work leaves your hands.
|
|
643
|
+
review_requested_of_me: "supervision",
|
|
644
|
+
review_given: "supervision",
|
|
645
|
+
pull_request_opened: "creation",
|
|
646
|
+
context_pressure_high: "risk",
|
|
647
|
+
agent_loop_long: "risk",
|
|
648
|
+
after_hours_ai_session: "risk",
|
|
649
|
+
compile_error: "risk",
|
|
650
|
+
tool_failure: "risk",
|
|
651
|
+
recovery_offline_period: "neutral",
|
|
652
|
+
context_compression_manual: "neutral",
|
|
653
|
+
context_compression_auto: "neutral",
|
|
654
|
+
editor_activity: "neutral",
|
|
655
|
+
// Semantic (agent-observed) — see SEMANTIC_WORK_SIGNAL_EVENT_TYPES.
|
|
656
|
+
approach_churn_detected: "risk",
|
|
657
|
+
goal_drift_detected: "risk",
|
|
658
|
+
progress_stalled: "risk",
|
|
659
|
+
progress_recovered: "neutral",
|
|
660
|
+
session_intention_declared: "neutral",
|
|
661
|
+
scope_change_declared: "neutral"
|
|
662
|
+
};
|
|
663
|
+
exports.ASCENDA_CONSENT_SCOPE = "ide_telemetry";
|
|
664
|
+
exports.ASCENDA_PROVENANCE = "ai_work_telemetry";
|
|
665
|
+
exports.ASCENDA_SEMANTIC_CONSENT_SCOPE = "semantic_work_signals";
|
|
666
|
+
exports.ASCENDA_COLLABORATION_CONSENT_SCOPE = "workflow_telemetry";
|
|
667
|
+
exports.ASCENDA_HISTORICAL_CONSENT_SCOPE = "historical_import";
|
|
668
|
+
exports.ASCENDA_SEMANTIC_PROVENANCE = "semantic_work_signals";
|
|
669
|
+
var metricKeys_1 = require_metricKeys();
|
|
670
|
+
Object.defineProperty(exports, "METRIC_KEYS", { enumerable: true, get: function() {
|
|
671
|
+
return metricKeys_1.METRIC_KEYS;
|
|
672
|
+
} });
|
|
673
|
+
Object.defineProperty(exports, "backendMetricKeys", { enumerable: true, get: function() {
|
|
674
|
+
return metricKeys_1.backendMetricKeys;
|
|
675
|
+
} });
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
|
|
297
679
|
// ../packages/tool-kit/out/payload.js
|
|
298
680
|
var require_payload = __commonJS({
|
|
299
681
|
"../packages/tool-kit/out/payload.js"(exports) {
|
|
300
682
|
"use strict";
|
|
301
683
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
684
|
+
exports.mintIdempotencyKey = mintIdempotencyKey;
|
|
302
685
|
exports.getString = getString;
|
|
303
686
|
exports.getNumber = getNumber;
|
|
304
687
|
exports.getNested = getNested;
|
|
@@ -307,6 +690,14 @@ var require_payload = __commonJS({
|
|
|
307
690
|
exports.inferOutcome = inferOutcome;
|
|
308
691
|
exports.outcomeForHook = outcomeForHook;
|
|
309
692
|
exports.looksLikeCorrection = looksLikeCorrection;
|
|
693
|
+
var node_crypto_1 = __require("node:crypto");
|
|
694
|
+
var tool_contract_1 = require_out();
|
|
695
|
+
function mintIdempotencyKey() {
|
|
696
|
+
const key = (0, node_crypto_1.randomUUID)();
|
|
697
|
+
if (key.length > tool_contract_1.IDEMPOTENCY_KEY_MAX_LENGTH)
|
|
698
|
+
throw new Error("idempotency key exceeds the wire limit");
|
|
699
|
+
return key;
|
|
700
|
+
}
|
|
310
701
|
function getString(input, keys) {
|
|
311
702
|
for (const key of keys) {
|
|
312
703
|
const value = input[key];
|
|
@@ -377,73 +768,6 @@ var require_payload = __commonJS({
|
|
|
377
768
|
}
|
|
378
769
|
});
|
|
379
770
|
|
|
380
|
-
// ../packages/tool-contract/out/index.js
|
|
381
|
-
var require_out = __commonJS({
|
|
382
|
-
"../packages/tool-contract/out/index.js"(exports) {
|
|
383
|
-
"use strict";
|
|
384
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
385
|
-
exports.ASCENDA_SEMANTIC_PROVENANCE = exports.ASCENDA_HISTORICAL_CONSENT_SCOPE = exports.ASCENDA_COLLABORATION_CONSENT_SCOPE = exports.ASCENDA_SEMANTIC_CONSENT_SCOPE = exports.ASCENDA_PROVENANCE = exports.ASCENDA_CONSENT_SCOPE = exports.EVENT_WORKLOAD_CATEGORY = exports.COLLABORATION_EVENT_TYPES = exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = void 0;
|
|
386
|
-
exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = [
|
|
387
|
-
"approach_churn_detected",
|
|
388
|
-
"goal_drift_detected",
|
|
389
|
-
"progress_stalled",
|
|
390
|
-
"progress_recovered",
|
|
391
|
-
"session_intention_declared",
|
|
392
|
-
"scope_change_declared"
|
|
393
|
-
];
|
|
394
|
-
exports.COLLABORATION_EVENT_TYPES = [
|
|
395
|
-
"review_requested_of_me",
|
|
396
|
-
"review_given",
|
|
397
|
-
"pull_request_opened"
|
|
398
|
-
];
|
|
399
|
-
exports.EVENT_WORKLOAD_CATEGORY = {
|
|
400
|
-
create_focus_session: "creation",
|
|
401
|
-
ai_prompt_submitted: "creation",
|
|
402
|
-
ai_generation_completed: "creation",
|
|
403
|
-
ai_file_write: "creation",
|
|
404
|
-
ai_file_edit: "creation",
|
|
405
|
-
editor_verification_activity: "verification",
|
|
406
|
-
compile_diagnostic: "verification",
|
|
407
|
-
editor_correction_activity: "supervision",
|
|
408
|
-
ai_correction_prompt: "supervision",
|
|
409
|
-
supervis_meeting_load: "supervision",
|
|
410
|
-
ai_tool_call_started: "supervision",
|
|
411
|
-
ai_tool_call_completed: "supervision",
|
|
412
|
-
ai_tool_call_failed: "supervision",
|
|
413
|
-
// Collaboration (the report's §4.2 collaboration family). Both review
|
|
414
|
-
// events are supervision: being asked to check work, and checking it, are
|
|
415
|
-
// the load the report's "verification overload" concern is about — the one
|
|
416
|
-
// that concentrates on senior engineers as a team adopts AI. Opening a pull
|
|
417
|
-
// request is creation: it is the point your own work leaves your hands.
|
|
418
|
-
review_requested_of_me: "supervision",
|
|
419
|
-
review_given: "supervision",
|
|
420
|
-
pull_request_opened: "creation",
|
|
421
|
-
context_pressure_high: "risk",
|
|
422
|
-
agent_loop_long: "risk",
|
|
423
|
-
after_hours_ai_session: "risk",
|
|
424
|
-
compile_error: "risk",
|
|
425
|
-
tool_failure: "risk",
|
|
426
|
-
recovery_offline_period: "neutral",
|
|
427
|
-
context_compression_manual: "neutral",
|
|
428
|
-
context_compression_auto: "neutral",
|
|
429
|
-
editor_activity: "neutral",
|
|
430
|
-
// Semantic (agent-observed) — see SEMANTIC_WORK_SIGNAL_EVENT_TYPES.
|
|
431
|
-
approach_churn_detected: "risk",
|
|
432
|
-
goal_drift_detected: "risk",
|
|
433
|
-
progress_stalled: "risk",
|
|
434
|
-
progress_recovered: "neutral",
|
|
435
|
-
session_intention_declared: "neutral",
|
|
436
|
-
scope_change_declared: "neutral"
|
|
437
|
-
};
|
|
438
|
-
exports.ASCENDA_CONSENT_SCOPE = "ide_telemetry";
|
|
439
|
-
exports.ASCENDA_PROVENANCE = "ai_work_telemetry";
|
|
440
|
-
exports.ASCENDA_SEMANTIC_CONSENT_SCOPE = "semantic_work_signals";
|
|
441
|
-
exports.ASCENDA_COLLABORATION_CONSENT_SCOPE = "workflow_telemetry";
|
|
442
|
-
exports.ASCENDA_HISTORICAL_CONSENT_SCOPE = "historical_import";
|
|
443
|
-
exports.ASCENDA_SEMANTIC_PROVENANCE = "semantic_work_signals";
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
|
|
447
771
|
// ../packages/tool-kit/out/eventLog.js
|
|
448
772
|
var require_eventLog = __commonJS({
|
|
449
773
|
"../packages/tool-kit/out/eventLog.js"(exports) {
|
|
@@ -544,6 +868,7 @@ var require_http = __commonJS({
|
|
|
544
868
|
exports.postToolEvent = postToolEvent;
|
|
545
869
|
exports.postToolEventsBatch = postToolEventsBatch;
|
|
546
870
|
exports.parseIngestResponse = parseIngestResponse;
|
|
871
|
+
var tool_contract_1 = require_out();
|
|
547
872
|
var AscendaApiError = class extends Error {
|
|
548
873
|
status;
|
|
549
874
|
errorCode;
|
|
@@ -586,6 +911,35 @@ var require_http = __commonJS({
|
|
|
586
911
|
throw new AscendaApiError(response.status, void 0, await response.text());
|
|
587
912
|
return await response.json();
|
|
588
913
|
}
|
|
914
|
+
function isDeliveredStatus(value) {
|
|
915
|
+
return typeof value === "string" && tool_contract_1.TOOL_EVENT_DELIVERED_STATUSES.includes(value);
|
|
916
|
+
}
|
|
917
|
+
function readSuccessBody(body) {
|
|
918
|
+
let parsed;
|
|
919
|
+
try {
|
|
920
|
+
parsed = JSON.parse(body);
|
|
921
|
+
} catch {
|
|
922
|
+
return { duplicates: 0 };
|
|
923
|
+
}
|
|
924
|
+
if (!parsed || typeof parsed !== "object")
|
|
925
|
+
return { duplicates: 0 };
|
|
926
|
+
const single = parsed.status;
|
|
927
|
+
if (isDeliveredStatus(single))
|
|
928
|
+
return { duplicates: single === "duplicate" ? 1 : 0 };
|
|
929
|
+
const raw = parsed.results;
|
|
930
|
+
if (!Array.isArray(raw))
|
|
931
|
+
return { duplicates: 0 };
|
|
932
|
+
const results = [];
|
|
933
|
+
for (const item of raw) {
|
|
934
|
+
if (!item || typeof item !== "object")
|
|
935
|
+
continue;
|
|
936
|
+
const { index, status, reason } = item;
|
|
937
|
+
if (typeof index !== "number" || typeof status !== "string")
|
|
938
|
+
continue;
|
|
939
|
+
results.push({ index, status, ...typeof reason === "string" ? { reason } : {} });
|
|
940
|
+
}
|
|
941
|
+
return { duplicates: results.filter((item) => item.status === "duplicate").length, results };
|
|
942
|
+
}
|
|
589
943
|
function isRetryableStatus(status) {
|
|
590
944
|
return status === 408 || status === 429 || status !== void 0 && status >= 500 && status <= 599;
|
|
591
945
|
}
|
|
@@ -612,8 +966,20 @@ var require_http = __commonJS({
|
|
|
612
966
|
}
|
|
613
967
|
}
|
|
614
968
|
async function parseIngestResponse(response) {
|
|
615
|
-
if (response.ok)
|
|
616
|
-
|
|
969
|
+
if (response.ok) {
|
|
970
|
+
const outcome = { result: "accepted", httpStatus: response.status };
|
|
971
|
+
let read = { duplicates: 0 };
|
|
972
|
+
try {
|
|
973
|
+
read = readSuccessBody(await response.text());
|
|
974
|
+
} catch {
|
|
975
|
+
read = { duplicates: 0 };
|
|
976
|
+
}
|
|
977
|
+
return {
|
|
978
|
+
...outcome,
|
|
979
|
+
...read.duplicates > 0 ? { duplicates: read.duplicates } : {},
|
|
980
|
+
...read.results !== void 0 ? { results: read.results } : {}
|
|
981
|
+
};
|
|
982
|
+
}
|
|
617
983
|
const body = await response.text();
|
|
618
984
|
let errorCode;
|
|
619
985
|
try {
|
|
@@ -675,15 +1041,20 @@ var require_tokenStore = __commonJS({
|
|
|
675
1041
|
};
|
|
676
1042
|
}();
|
|
677
1043
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1044
|
+
exports.ascendaHome = ascendaHome;
|
|
678
1045
|
exports.defaultTokenFilePath = defaultTokenFilePath2;
|
|
679
1046
|
exports.persistEventWriteToken = persistEventWriteToken2;
|
|
1047
|
+
exports.listPersistedToolInstallationIds = listPersistedToolInstallationIds;
|
|
680
1048
|
exports.readTokenFile = readTokenFile2;
|
|
681
1049
|
exports.sanitizeFilePart = sanitizeFilePart;
|
|
682
1050
|
var fs = __importStar(__require("fs"));
|
|
683
1051
|
var os = __importStar(__require("os"));
|
|
684
1052
|
var path = __importStar(__require("path"));
|
|
1053
|
+
function ascendaHome() {
|
|
1054
|
+
return process.env.ASCENDA_HOME ?? path.join(os.homedir(), ".ascenda");
|
|
1055
|
+
}
|
|
685
1056
|
function defaultTokenFilePath2(toolInstallationId) {
|
|
686
|
-
return path.join(
|
|
1057
|
+
return path.join(ascendaHome(), "tokens", sanitizeFilePart(toolInstallationId));
|
|
687
1058
|
}
|
|
688
1059
|
function persistEventWriteToken2(tokenFilePath, token) {
|
|
689
1060
|
const dir = path.dirname(tokenFilePath);
|
|
@@ -694,6 +1065,32 @@ var require_tokenStore = __commonJS({
|
|
|
694
1065
|
fs.chmodSync(tokenFilePath, 384);
|
|
695
1066
|
}
|
|
696
1067
|
}
|
|
1068
|
+
function listPersistedToolInstallationIds(toolType) {
|
|
1069
|
+
const prefix = `${sanitizeFilePart(toolType)}_`;
|
|
1070
|
+
const dir = path.join(ascendaHome(), "tokens");
|
|
1071
|
+
let names;
|
|
1072
|
+
try {
|
|
1073
|
+
names = fs.readdirSync(dir);
|
|
1074
|
+
} catch {
|
|
1075
|
+
return [];
|
|
1076
|
+
}
|
|
1077
|
+
const ids = [];
|
|
1078
|
+
for (const name of names.sort()) {
|
|
1079
|
+
if (!name.startsWith(prefix) || name.length === prefix.length)
|
|
1080
|
+
continue;
|
|
1081
|
+
const file = path.join(dir, name);
|
|
1082
|
+
try {
|
|
1083
|
+
if (!fs.statSync(file).isFile())
|
|
1084
|
+
continue;
|
|
1085
|
+
} catch {
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
if (readTokenFile2(file) === void 0)
|
|
1089
|
+
continue;
|
|
1090
|
+
ids.push(`${toolType}:${name.slice(prefix.length)}`);
|
|
1091
|
+
}
|
|
1092
|
+
return ids;
|
|
1093
|
+
}
|
|
697
1094
|
function readTokenFile2(tokenFilePath) {
|
|
698
1095
|
try {
|
|
699
1096
|
if (!fs.existsSync(tokenFilePath))
|
|
@@ -753,8 +1150,11 @@ var require_stateStore = __commonJS({
|
|
|
753
1150
|
}();
|
|
754
1151
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
755
1152
|
exports.defaultStateFilePath = defaultStateFilePath;
|
|
1153
|
+
exports.unresolvedToolInstallationId = unresolvedToolInstallationId;
|
|
1154
|
+
exports.unresolvedStateFilePath = unresolvedStateFilePath;
|
|
756
1155
|
exports.readCollectorState = readCollectorState;
|
|
757
1156
|
exports.recordSendOutcome = recordSendOutcome;
|
|
1157
|
+
exports.recordOutboxDiscard = recordOutboxDiscard;
|
|
758
1158
|
exports.shouldAnnounceFailure = shouldAnnounceFailure;
|
|
759
1159
|
exports.markFailureNotified = markFailureNotified;
|
|
760
1160
|
var fs = __importStar(__require("fs"));
|
|
@@ -766,6 +1166,12 @@ var require_stateStore = __commonJS({
|
|
|
766
1166
|
const base2 = dir ? dir : path.join(os.homedir(), ".ascenda", "state");
|
|
767
1167
|
return path.join(base2, `${(0, tokenStore_1.sanitizeFilePart)(toolInstallationId)}.json`);
|
|
768
1168
|
}
|
|
1169
|
+
function unresolvedToolInstallationId(toolType) {
|
|
1170
|
+
return `${toolType}:unresolved`;
|
|
1171
|
+
}
|
|
1172
|
+
function unresolvedStateFilePath(toolType) {
|
|
1173
|
+
return defaultStateFilePath(unresolvedToolInstallationId(toolType));
|
|
1174
|
+
}
|
|
769
1175
|
function readCollectorState(stateFilePath) {
|
|
770
1176
|
try {
|
|
771
1177
|
if (!fs.existsSync(stateFilePath))
|
|
@@ -798,7 +1204,28 @@ var require_stateStore = __commonJS({
|
|
|
798
1204
|
// the one already open. Carrying `notifiedFailingSince` across a
|
|
799
1205
|
// continuing episode is what keeps the notice to once per outage.
|
|
800
1206
|
...accepted ? {} : { failingSince: previous?.failingSince ?? now },
|
|
801
|
-
...accepted ? {} : previous?.notifiedFailingSince !== void 0 ? { notifiedFailingSince: previous.notifiedFailingSince } : {}
|
|
1207
|
+
...accepted ? {} : previous?.notifiedFailingSince !== void 0 ? { notifiedFailingSince: previous.notifiedFailingSince } : {},
|
|
1208
|
+
// Cumulative by design: a send outcome, success included, never erases
|
|
1209
|
+
// the record of what the outbox had to throw away.
|
|
1210
|
+
...previous?.outboxDiscarded !== void 0 ? { outboxDiscarded: previous.outboxDiscarded } : {}
|
|
1211
|
+
};
|
|
1212
|
+
writeStateFile(stateFilePath, next);
|
|
1213
|
+
return next;
|
|
1214
|
+
}
|
|
1215
|
+
function recordOutboxDiscard(stateFilePath, toolInstallationId, discard) {
|
|
1216
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1217
|
+
const previous = readCollectorState(stateFilePath);
|
|
1218
|
+
const next = {
|
|
1219
|
+
...previous ?? { lastAttemptAt: now, consecutiveFailures: 0 },
|
|
1220
|
+
toolInstallationId,
|
|
1221
|
+
lastOutcome: "outbox_discarded",
|
|
1222
|
+
outboxDiscarded: {
|
|
1223
|
+
total: (previous?.outboxDiscarded?.total ?? 0) + discard.count,
|
|
1224
|
+
lastAt: now,
|
|
1225
|
+
lastCount: discard.count,
|
|
1226
|
+
lastReasons: discard.reasons,
|
|
1227
|
+
...discard.oldestQueuedAt !== void 0 ? { lastOldestQueuedAt: discard.oldestQueuedAt } : {}
|
|
1228
|
+
}
|
|
802
1229
|
};
|
|
803
1230
|
writeStateFile(stateFilePath, next);
|
|
804
1231
|
return next;
|
|
@@ -838,6 +1265,226 @@ var require_stateStore = __commonJS({
|
|
|
838
1265
|
}
|
|
839
1266
|
});
|
|
840
1267
|
|
|
1268
|
+
// ../packages/tool-kit/out/outbox.js
|
|
1269
|
+
var require_outbox = __commonJS({
|
|
1270
|
+
"../packages/tool-kit/out/outbox.js"(exports) {
|
|
1271
|
+
"use strict";
|
|
1272
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1273
|
+
if (k2 === void 0) k2 = k;
|
|
1274
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1275
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1276
|
+
desc = { enumerable: true, get: function() {
|
|
1277
|
+
return m[k];
|
|
1278
|
+
} };
|
|
1279
|
+
}
|
|
1280
|
+
Object.defineProperty(o, k2, desc);
|
|
1281
|
+
} : function(o, m, k, k2) {
|
|
1282
|
+
if (k2 === void 0) k2 = k;
|
|
1283
|
+
o[k2] = m[k];
|
|
1284
|
+
});
|
|
1285
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
1286
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1287
|
+
} : function(o, v) {
|
|
1288
|
+
o["default"] = v;
|
|
1289
|
+
});
|
|
1290
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
1291
|
+
var ownKeys = function(o) {
|
|
1292
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1293
|
+
var ar = [];
|
|
1294
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1295
|
+
return ar;
|
|
1296
|
+
};
|
|
1297
|
+
return ownKeys(o);
|
|
1298
|
+
};
|
|
1299
|
+
return function(mod) {
|
|
1300
|
+
if (mod && mod.__esModule) return mod;
|
|
1301
|
+
var result = {};
|
|
1302
|
+
if (mod != null) {
|
|
1303
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1304
|
+
}
|
|
1305
|
+
__setModuleDefault(result, mod);
|
|
1306
|
+
return result;
|
|
1307
|
+
};
|
|
1308
|
+
}();
|
|
1309
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1310
|
+
exports.DEFAULT_OUTBOX_DRAIN_BATCH_SIZE = exports.DEFAULT_OUTBOX_MAX_AGE_MS = exports.DEFAULT_OUTBOX_MAX_ENTRIES = exports.OUTBOX_DRAIN_ENV_VAR = void 0;
|
|
1311
|
+
exports.outboxDrainEnabled = outboxDrainEnabled;
|
|
1312
|
+
exports.defaultOutboxFilePath = defaultOutboxFilePath;
|
|
1313
|
+
exports.appendToOutbox = appendToOutbox;
|
|
1314
|
+
exports.readOutboxSummary = readOutboxSummary;
|
|
1315
|
+
exports.claimOutbox = claimOutbox;
|
|
1316
|
+
exports.enforceOutboxBounds = enforceOutboxBounds;
|
|
1317
|
+
var fs = __importStar(__require("fs"));
|
|
1318
|
+
var path = __importStar(__require("path"));
|
|
1319
|
+
var stateStore_1 = require_stateStore();
|
|
1320
|
+
var tokenStore_1 = require_tokenStore();
|
|
1321
|
+
exports.OUTBOX_DRAIN_ENV_VAR = "ASCENDA_OUTBOX_DRAIN";
|
|
1322
|
+
exports.DEFAULT_OUTBOX_MAX_ENTRIES = 1e4;
|
|
1323
|
+
exports.DEFAULT_OUTBOX_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
1324
|
+
exports.DEFAULT_OUTBOX_DRAIN_BATCH_SIZE = 100;
|
|
1325
|
+
var ORPHANED_CLAIM_AGE_MS = 6e4;
|
|
1326
|
+
var CLAIM_SUFFIX = ".draining";
|
|
1327
|
+
function outboxDrainEnabled(env = process.env) {
|
|
1328
|
+
const value = env[exports.OUTBOX_DRAIN_ENV_VAR]?.trim().toLowerCase();
|
|
1329
|
+
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
1330
|
+
}
|
|
1331
|
+
function defaultOutboxFilePath(toolInstallationId) {
|
|
1332
|
+
const dir = path.dirname((0, stateStore_1.defaultStateFilePath)(toolInstallationId));
|
|
1333
|
+
return path.join(dir, `${(0, tokenStore_1.sanitizeFilePart)(toolInstallationId)}.outbox.jsonl`);
|
|
1334
|
+
}
|
|
1335
|
+
function appendToOutbox(outboxFilePath, payload, now = /* @__PURE__ */ new Date()) {
|
|
1336
|
+
try {
|
|
1337
|
+
const dir = path.dirname(outboxFilePath);
|
|
1338
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1339
|
+
const entry = { queuedAt: now.toISOString(), payload };
|
|
1340
|
+
fs.appendFileSync(outboxFilePath, `${JSON.stringify(entry)}
|
|
1341
|
+
`, { encoding: "utf8", mode: 384 });
|
|
1342
|
+
if (process.platform !== "win32")
|
|
1343
|
+
fs.chmodSync(outboxFilePath, 384);
|
|
1344
|
+
return true;
|
|
1345
|
+
} catch {
|
|
1346
|
+
return false;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
function readOutboxSummary(outboxFilePath) {
|
|
1350
|
+
const files = [outboxFilePath, ...listClaimFiles(outboxFilePath)].filter((file) => fs.existsSync(file));
|
|
1351
|
+
if (files.length === 0)
|
|
1352
|
+
return void 0;
|
|
1353
|
+
let depth = 0;
|
|
1354
|
+
let unreadableLines = 0;
|
|
1355
|
+
let oldestQueuedAt;
|
|
1356
|
+
for (const file of files) {
|
|
1357
|
+
const { entries, unreadable } = readEntries(file);
|
|
1358
|
+
depth += entries.length;
|
|
1359
|
+
unreadableLines += unreadable;
|
|
1360
|
+
for (const entry of entries) {
|
|
1361
|
+
if (oldestQueuedAt === void 0 || entry.queuedAt < oldestQueuedAt)
|
|
1362
|
+
oldestQueuedAt = entry.queuedAt;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
return { depth, unreadableLines, ...oldestQueuedAt !== void 0 ? { oldestQueuedAt } : {} };
|
|
1366
|
+
}
|
|
1367
|
+
function claimOutbox(outboxFilePath, now = Date.now()) {
|
|
1368
|
+
const claimPath = `${outboxFilePath}.${process.pid}${CLAIM_SUFFIX}`;
|
|
1369
|
+
const claimed = [];
|
|
1370
|
+
try {
|
|
1371
|
+
fs.renameSync(outboxFilePath, claimPath);
|
|
1372
|
+
claimed.push(claimPath);
|
|
1373
|
+
} catch {
|
|
1374
|
+
}
|
|
1375
|
+
let orphanIndex = 0;
|
|
1376
|
+
for (const orphan of listClaimFiles(outboxFilePath)) {
|
|
1377
|
+
if (claimed.includes(orphan))
|
|
1378
|
+
continue;
|
|
1379
|
+
try {
|
|
1380
|
+
if (now - fs.statSync(orphan).mtimeMs < ORPHANED_CLAIM_AGE_MS)
|
|
1381
|
+
continue;
|
|
1382
|
+
const mine = `${claimPath}.${orphanIndex++}`;
|
|
1383
|
+
fs.renameSync(orphan, mine);
|
|
1384
|
+
claimed.push(mine);
|
|
1385
|
+
} catch {
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
if (claimed.length === 0)
|
|
1389
|
+
return void 0;
|
|
1390
|
+
const entries = [];
|
|
1391
|
+
let unreadable = 0;
|
|
1392
|
+
for (const file of claimed) {
|
|
1393
|
+
const read = readEntries(file);
|
|
1394
|
+
entries.push(...read.entries);
|
|
1395
|
+
unreadable += read.unreadable;
|
|
1396
|
+
}
|
|
1397
|
+
entries.sort((a, b) => a.queuedAt < b.queuedAt ? -1 : a.queuedAt > b.queuedAt ? 1 : 0);
|
|
1398
|
+
let released = false;
|
|
1399
|
+
return {
|
|
1400
|
+
entries,
|
|
1401
|
+
unreadable,
|
|
1402
|
+
release(remainder) {
|
|
1403
|
+
if (released)
|
|
1404
|
+
return;
|
|
1405
|
+
released = true;
|
|
1406
|
+
if (remainder.length > 0) {
|
|
1407
|
+
try {
|
|
1408
|
+
fs.mkdirSync(path.dirname(outboxFilePath), { recursive: true, mode: 448 });
|
|
1409
|
+
fs.appendFileSync(outboxFilePath, remainder.map((entry) => `${JSON.stringify(entry)}
|
|
1410
|
+
`).join(""), { encoding: "utf8", mode: 384 });
|
|
1411
|
+
if (process.platform !== "win32")
|
|
1412
|
+
fs.chmodSync(outboxFilePath, 384);
|
|
1413
|
+
} catch {
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
for (const file of claimed) {
|
|
1418
|
+
try {
|
|
1419
|
+
fs.unlinkSync(file);
|
|
1420
|
+
} catch {
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
function enforceOutboxBounds(entries, bounds, now = Date.now()) {
|
|
1427
|
+
const reasons = {};
|
|
1428
|
+
let oldestQueuedAt;
|
|
1429
|
+
const cutoff = new Date(now - bounds.maxAgeMs).toISOString();
|
|
1430
|
+
const fresh = [];
|
|
1431
|
+
for (const entry of entries) {
|
|
1432
|
+
if (entry.queuedAt < cutoff) {
|
|
1433
|
+
reasons.age = (reasons.age ?? 0) + 1;
|
|
1434
|
+
if (oldestQueuedAt === void 0 || entry.queuedAt < oldestQueuedAt)
|
|
1435
|
+
oldestQueuedAt = entry.queuedAt;
|
|
1436
|
+
} else {
|
|
1437
|
+
fresh.push(entry);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
const excess = Math.max(0, fresh.length - bounds.maxEntries);
|
|
1441
|
+
if (excess > 0) {
|
|
1442
|
+
reasons.count = excess;
|
|
1443
|
+
const first = fresh[0]?.queuedAt;
|
|
1444
|
+
if (first !== void 0 && (oldestQueuedAt === void 0 || first < oldestQueuedAt))
|
|
1445
|
+
oldestQueuedAt = first;
|
|
1446
|
+
}
|
|
1447
|
+
const kept = excess > 0 ? fresh.slice(excess) : fresh;
|
|
1448
|
+
const count = Object.values(reasons).reduce((sum, n) => sum + (n ?? 0), 0);
|
|
1449
|
+
return { kept, discarded: { count, reasons, ...oldestQueuedAt !== void 0 ? { oldestQueuedAt } : {} } };
|
|
1450
|
+
}
|
|
1451
|
+
function listClaimFiles(outboxFilePath) {
|
|
1452
|
+
const dir = path.dirname(outboxFilePath);
|
|
1453
|
+
const prefix = `${path.basename(outboxFilePath)}.`;
|
|
1454
|
+
try {
|
|
1455
|
+
return fs.readdirSync(dir).filter((name) => name.startsWith(prefix) && name.includes(CLAIM_SUFFIX)).map((name) => path.join(dir, name)).sort();
|
|
1456
|
+
} catch {
|
|
1457
|
+
return [];
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
function readEntries(file) {
|
|
1461
|
+
let raw;
|
|
1462
|
+
try {
|
|
1463
|
+
raw = fs.readFileSync(file, "utf8");
|
|
1464
|
+
} catch {
|
|
1465
|
+
return { entries: [], unreadable: 0 };
|
|
1466
|
+
}
|
|
1467
|
+
const entries = [];
|
|
1468
|
+
let unreadable = 0;
|
|
1469
|
+
for (const line of raw.split("\n")) {
|
|
1470
|
+
if (!line.trim())
|
|
1471
|
+
continue;
|
|
1472
|
+
try {
|
|
1473
|
+
const parsed = JSON.parse(line);
|
|
1474
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.queuedAt !== "string" || !parsed.payload || typeof parsed.payload !== "object") {
|
|
1475
|
+
unreadable += 1;
|
|
1476
|
+
continue;
|
|
1477
|
+
}
|
|
1478
|
+
entries.push({ queuedAt: parsed.queuedAt, payload: parsed.payload });
|
|
1479
|
+
} catch {
|
|
1480
|
+
unreadable += 1;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
return { entries, unreadable };
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
|
|
841
1488
|
// ../packages/tool-kit/out/eventSender.js
|
|
842
1489
|
var require_eventSender = __commonJS({
|
|
843
1490
|
"../packages/tool-kit/out/eventSender.js"(exports) {
|
|
@@ -849,8 +1496,10 @@ var require_eventSender = __commonJS({
|
|
|
849
1496
|
var tool_contract_1 = require_out();
|
|
850
1497
|
var eventLog_1 = require_eventLog();
|
|
851
1498
|
var http_1 = require_http();
|
|
1499
|
+
var outbox_1 = require_outbox();
|
|
852
1500
|
var tokenStore_1 = require_tokenStore();
|
|
853
1501
|
var stateStore_1 = require_stateStore();
|
|
1502
|
+
var payload_1 = require_payload();
|
|
854
1503
|
var AscendaSemanticEventError = class extends Error {
|
|
855
1504
|
constructor(message) {
|
|
856
1505
|
super(message);
|
|
@@ -863,6 +1512,7 @@ var require_eventSender = __commonJS({
|
|
|
863
1512
|
toolInstallationId: identity.toolInstallationId,
|
|
864
1513
|
source: identity.source,
|
|
865
1514
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1515
|
+
idempotencyKey: (0, payload_1.mintIdempotencyKey)(),
|
|
866
1516
|
utcOffsetMinutes: (0, afterHours_1.utcOffsetMinutesAt)(/* @__PURE__ */ new Date()),
|
|
867
1517
|
sessionId: identity.sessionId ?? void 0,
|
|
868
1518
|
workspaceHash: identity.workspaceHash ?? void 0,
|
|
@@ -880,6 +1530,9 @@ var require_eventSender = __commonJS({
|
|
|
880
1530
|
config;
|
|
881
1531
|
eventWriteToken;
|
|
882
1532
|
lastState;
|
|
1533
|
+
lastDrain;
|
|
1534
|
+
/** One outbox pass per sender, i.e. per hook process. The hook is on the user's critical path. */
|
|
1535
|
+
outboxServiced = false;
|
|
883
1536
|
constructor(config) {
|
|
884
1537
|
this.config = config;
|
|
885
1538
|
this.eventWriteToken = config.eventWriteToken;
|
|
@@ -918,6 +1571,7 @@ var require_eventSender = __commonJS({
|
|
|
918
1571
|
source: this.config.source,
|
|
919
1572
|
eventType: mapped.eventType,
|
|
920
1573
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1574
|
+
idempotencyKey: (0, payload_1.mintIdempotencyKey)(),
|
|
921
1575
|
utcOffsetMinutes: (0, afterHours_1.utcOffsetMinutesAt)(/* @__PURE__ */ new Date()),
|
|
922
1576
|
severity: "low",
|
|
923
1577
|
sessionId: this.config.sessionId ?? void 0,
|
|
@@ -948,6 +1602,7 @@ var require_eventSender = __commonJS({
|
|
|
948
1602
|
source: this.config.source,
|
|
949
1603
|
eventType: mapped.eventType,
|
|
950
1604
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1605
|
+
idempotencyKey: (0, payload_1.mintIdempotencyKey)(),
|
|
951
1606
|
utcOffsetMinutes: (0, afterHours_1.utcOffsetMinutesAt)(/* @__PURE__ */ new Date()),
|
|
952
1607
|
severity: "low",
|
|
953
1608
|
sessionId: this.config.sessionId ?? void 0,
|
|
@@ -966,15 +1621,30 @@ var require_eventSender = __commonJS({
|
|
|
966
1621
|
* deliberate: Claude Code, Codex, the GitHub collector and the MCP server all
|
|
967
1622
|
* send through this method, and the defect being fixed showed up in three
|
|
968
1623
|
* separate components because each was left to notice its own failures.
|
|
1624
|
+
*
|
|
1625
|
+
* The outbox is serviced first, once per process. If that pass just watched
|
|
1626
|
+
* the ingest door refuse a batch, the live event is not offered to the same
|
|
1627
|
+
* door a second time in the same instant: it inherits the pass's outcome,
|
|
1628
|
+
* and a retryable one puts it straight in the queue. That is what keeps a
|
|
1629
|
+
* hook during an outage to one bounded round trip instead of three.
|
|
969
1630
|
*/
|
|
970
1631
|
async post(payload) {
|
|
971
|
-
const
|
|
1632
|
+
const halted = await this.serviceOutbox();
|
|
1633
|
+
let outcome;
|
|
1634
|
+
let queued = false;
|
|
1635
|
+
if (halted) {
|
|
1636
|
+
outcome = halted;
|
|
1637
|
+
queued = this.isRetryable(outcome) && this.enqueue(payload);
|
|
1638
|
+
} else {
|
|
1639
|
+
outcome = await this.attempt(payload);
|
|
1640
|
+
queued = this.isRetryable(outcome) && this.enqueue(payload);
|
|
1641
|
+
}
|
|
972
1642
|
this.lastState = (0, stateStore_1.recordSendOutcome)(this.stateFilePath(), this.config.toolInstallationId, outcome.result, {
|
|
973
1643
|
httpStatus: outcome.httpStatus,
|
|
974
1644
|
errorCode: outcome.errorCode,
|
|
975
|
-
detail: outcome.detail
|
|
1645
|
+
detail: queued ? withNote(outcome.detail, "queued in outbox") : outcome.detail
|
|
976
1646
|
});
|
|
977
|
-
this.log(payload, outcome.result);
|
|
1647
|
+
this.log(payload, outcome.result, queued ? "queued" : void 0);
|
|
978
1648
|
return outcome.result;
|
|
979
1649
|
}
|
|
980
1650
|
/**
|
|
@@ -983,6 +1653,15 @@ var require_eventSender = __commonJS({
|
|
|
983
1653
|
* error gets one retry, because the common cases (a restarting instance, a
|
|
984
1654
|
* proxy blip, a 429) clear in well under a second and the alternative is
|
|
985
1655
|
* losing the event outright.
|
|
1656
|
+
*
|
|
1657
|
+
* Both recoveries resend the same `payload` object, so the `idempotencyKey`
|
|
1658
|
+
* minted at construction is what the server sees on every attempt. That is
|
|
1659
|
+
* what lets a retry of a request the server actually processed (a timeout
|
|
1660
|
+
* after the write, a 502 from a proxy in front of a 200) come back
|
|
1661
|
+
* `duplicate` instead of landing twice. Never rebuild the payload here.
|
|
1662
|
+
*
|
|
1663
|
+
* When the retry fails too, the caller queues the payload: anything longer
|
|
1664
|
+
* than the pause here is the outbox's job, not another in-process wait.
|
|
986
1665
|
*/
|
|
987
1666
|
async attempt(payload) {
|
|
988
1667
|
const outcome = await (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
@@ -991,12 +1670,138 @@ var require_eventSender = __commonJS({
|
|
|
991
1670
|
return outcome;
|
|
992
1671
|
return (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
993
1672
|
}
|
|
994
|
-
if (
|
|
1673
|
+
if (this.isRetryable(outcome)) {
|
|
995
1674
|
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
|
996
1675
|
return (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
997
1676
|
}
|
|
998
1677
|
return outcome;
|
|
999
1678
|
}
|
|
1679
|
+
/** A failure that never reached a verdict. Replaying can change the answer. */
|
|
1680
|
+
isRetryable(outcome) {
|
|
1681
|
+
return outcome.result === "transport_error" && (outcome.httpStatus === void 0 || (0, http_1.isRetryableStatus)(outcome.httpStatus));
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Keeps a refused payload for a later drain. Returns whether it is now on
|
|
1685
|
+
* disk; when it is not (read-only home, full disk) the event is lost and the
|
|
1686
|
+
* journal's detail says so instead of implying it was kept.
|
|
1687
|
+
*/
|
|
1688
|
+
enqueue(payload) {
|
|
1689
|
+
return (0, outbox_1.appendToOutbox)(this.outboxFilePath(), payload);
|
|
1690
|
+
}
|
|
1691
|
+
/**
|
|
1692
|
+
* One pass over the outbox: claim it, apply the bounds, and — when sending
|
|
1693
|
+
* is enabled — offer one batch, oldest first, to the batch door.
|
|
1694
|
+
*
|
|
1695
|
+
* Entries are deleted on `accepted` or `duplicate`, decided on `status`
|
|
1696
|
+
* alone; `reason` is for a person reading their logs. A per-item `rejected`
|
|
1697
|
+
* is a verdict, and replaying a verdict cannot change it, so those are
|
|
1698
|
+
* discarded and journaled rather than kept forever. A whole-batch
|
|
1699
|
+
* `validation_failed` is the same verdict for every item. Anything else
|
|
1700
|
+
* stops the pass with everything still on disk, and is returned so the live
|
|
1701
|
+
* send can skip a door that just refused.
|
|
1702
|
+
*
|
|
1703
|
+
* Never loops, never backs off, never sends more than one batch: the next
|
|
1704
|
+
* hook invocation is usually seconds away, and a hook sitting in a retry
|
|
1705
|
+
* loop delays the tool call the user is waiting on.
|
|
1706
|
+
*/
|
|
1707
|
+
async serviceOutbox() {
|
|
1708
|
+
if (this.outboxServiced)
|
|
1709
|
+
return void 0;
|
|
1710
|
+
this.outboxServiced = true;
|
|
1711
|
+
const sendEnabled = this.config.outboxDrain ?? (0, outbox_1.outboxDrainEnabled)();
|
|
1712
|
+
const claimed = (0, outbox_1.claimOutbox)(this.outboxFilePath());
|
|
1713
|
+
if (!claimed) {
|
|
1714
|
+
this.lastDrain = { found: 0, discarded: 0, delivered: 0, remaining: 0, sendEnabled };
|
|
1715
|
+
return void 0;
|
|
1716
|
+
}
|
|
1717
|
+
const found = claimed.entries.length + claimed.unreadable;
|
|
1718
|
+
const { kept, discarded } = (0, outbox_1.enforceOutboxBounds)(claimed.entries, {
|
|
1719
|
+
maxEntries: this.config.outboxMaxEntries ?? outbox_1.DEFAULT_OUTBOX_MAX_ENTRIES,
|
|
1720
|
+
maxAgeMs: this.config.outboxMaxAgeMs ?? outbox_1.DEFAULT_OUTBOX_MAX_AGE_MS
|
|
1721
|
+
});
|
|
1722
|
+
if (claimed.unreadable > 0) {
|
|
1723
|
+
discarded.count += claimed.unreadable;
|
|
1724
|
+
discarded.reasons.unreadable = claimed.unreadable;
|
|
1725
|
+
}
|
|
1726
|
+
let discardedTotal = this.journalDiscard(discarded);
|
|
1727
|
+
if (!sendEnabled || kept.length === 0) {
|
|
1728
|
+
claimed.release(kept);
|
|
1729
|
+
this.lastDrain = { found, discarded: discardedTotal, delivered: 0, remaining: kept.length, sendEnabled };
|
|
1730
|
+
return void 0;
|
|
1731
|
+
}
|
|
1732
|
+
const batchSize = this.config.outboxDrainBatchSize ?? outbox_1.DEFAULT_OUTBOX_DRAIN_BATCH_SIZE;
|
|
1733
|
+
const batch = kept.slice(0, batchSize);
|
|
1734
|
+
const rest = kept.slice(batchSize);
|
|
1735
|
+
const outcome = await this.attemptBatch(batch.map((entry) => entry.payload));
|
|
1736
|
+
let delivered = [];
|
|
1737
|
+
let rejected = [];
|
|
1738
|
+
let undecided = [];
|
|
1739
|
+
let halted;
|
|
1740
|
+
if (outcome.result === "accepted") {
|
|
1741
|
+
if (outcome.results === void 0) {
|
|
1742
|
+
delivered = batch;
|
|
1743
|
+
} else {
|
|
1744
|
+
const byIndex = new Map(outcome.results.map((item) => [item.index, item.status]));
|
|
1745
|
+
for (const [index, entry] of batch.entries()) {
|
|
1746
|
+
const status = byIndex.get(index);
|
|
1747
|
+
if (status !== void 0 && tool_contract_1.TOOL_EVENT_DELIVERED_STATUSES.includes(status))
|
|
1748
|
+
delivered.push(entry);
|
|
1749
|
+
else if (status === "rejected")
|
|
1750
|
+
rejected.push(entry);
|
|
1751
|
+
else
|
|
1752
|
+
undecided.push(entry);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
} else if (outcome.result === "validation_failed") {
|
|
1756
|
+
rejected = batch;
|
|
1757
|
+
} else {
|
|
1758
|
+
undecided = batch;
|
|
1759
|
+
halted = outcome;
|
|
1760
|
+
}
|
|
1761
|
+
if (rejected.length > 0) {
|
|
1762
|
+
discardedTotal += this.journalDiscard({ count: rejected.length, reasons: { rejected: rejected.length }, oldestQueuedAt: rejected[0]?.queuedAt });
|
|
1763
|
+
}
|
|
1764
|
+
if (!halted) {
|
|
1765
|
+
this.lastState = (0, stateStore_1.recordSendOutcome)(this.stateFilePath(), this.config.toolInstallationId, outcome.result, {
|
|
1766
|
+
httpStatus: outcome.httpStatus,
|
|
1767
|
+
errorCode: outcome.errorCode,
|
|
1768
|
+
detail: withNote(outcome.detail, `outbox drain: ${delivered.length} delivered`)
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1771
|
+
for (const entry of delivered)
|
|
1772
|
+
this.log(entry.payload, "accepted", "drained");
|
|
1773
|
+
const remainder = [...undecided, ...rest];
|
|
1774
|
+
claimed.release(remainder);
|
|
1775
|
+
this.lastDrain = {
|
|
1776
|
+
found,
|
|
1777
|
+
discarded: discardedTotal,
|
|
1778
|
+
delivered: delivered.length,
|
|
1779
|
+
remaining: remainder.length,
|
|
1780
|
+
sendEnabled,
|
|
1781
|
+
...halted ? { halted: halted.result } : {}
|
|
1782
|
+
};
|
|
1783
|
+
return halted;
|
|
1784
|
+
}
|
|
1785
|
+
/** The batch door, with the same single token renewal as the live path and no in-process retry. */
|
|
1786
|
+
async attemptBatch(payloads) {
|
|
1787
|
+
const outcome = await (0, http_1.postToolEventsBatch)(this.config.apiBaseUrl, this.eventWriteToken, payloads, this.signal());
|
|
1788
|
+
if (outcome.result !== "auth_failed")
|
|
1789
|
+
return outcome;
|
|
1790
|
+
if (!await this.renewEventToken())
|
|
1791
|
+
return outcome;
|
|
1792
|
+
return (0, http_1.postToolEventsBatch)(this.config.apiBaseUrl, this.eventWriteToken, payloads, this.signal());
|
|
1793
|
+
}
|
|
1794
|
+
journalDiscard(discard) {
|
|
1795
|
+
if (discard.count === 0)
|
|
1796
|
+
return 0;
|
|
1797
|
+
const reasons = discard.reasons;
|
|
1798
|
+
this.lastState = (0, stateStore_1.recordOutboxDiscard)(this.stateFilePath(), this.config.toolInstallationId, {
|
|
1799
|
+
count: discard.count,
|
|
1800
|
+
reasons,
|
|
1801
|
+
oldestQueuedAt: discard.oldestQueuedAt
|
|
1802
|
+
});
|
|
1803
|
+
return discard.count;
|
|
1804
|
+
}
|
|
1000
1805
|
/**
|
|
1001
1806
|
* The state written by the most recent send, so a caller can decide whether
|
|
1002
1807
|
* to surface a one-time notice without re-reading the journal it just wrote.
|
|
@@ -1004,10 +1809,16 @@ var require_eventSender = __commonJS({
|
|
|
1004
1809
|
get state() {
|
|
1005
1810
|
return this.lastState;
|
|
1006
1811
|
}
|
|
1812
|
+
/** What this sender's one outbox pass did; undefined before the first send. */
|
|
1813
|
+
get drain() {
|
|
1814
|
+
return this.lastDrain;
|
|
1815
|
+
}
|
|
1007
1816
|
stateFilePath() {
|
|
1008
1817
|
return this.config.stateFilePath ?? (0, stateStore_1.defaultStateFilePath)(this.config.toolInstallationId);
|
|
1009
1818
|
}
|
|
1010
|
-
|
|
1819
|
+
outboxFilePath() {
|
|
1820
|
+
return this.config.outboxFilePath ?? (0, outbox_1.defaultOutboxFilePath)(this.config.toolInstallationId);
|
|
1821
|
+
}
|
|
1011
1822
|
/**
|
|
1012
1823
|
* Every send path funnels through {@link post}, so semantic and
|
|
1013
1824
|
* collaboration signals are logged on the same terms as host events — the
|
|
@@ -1017,12 +1828,13 @@ var require_eventSender = __commonJS({
|
|
|
1017
1828
|
* It is now `transport_error` through the ordinary path, because the
|
|
1018
1829
|
* transport returns that outcome instead of throwing.
|
|
1019
1830
|
*/
|
|
1020
|
-
log(payload, delivery) {
|
|
1831
|
+
log(payload, delivery, outbox) {
|
|
1021
1832
|
const logFile = this.config.eventLogFile === void 0 ? (0, eventLog_1.resolveEventLogPath)() : this.config.eventLogFile;
|
|
1022
1833
|
if (!logFile)
|
|
1023
1834
|
return;
|
|
1024
|
-
(0, eventLog_1.appendEventLog)(logFile, { loggedAt: (/* @__PURE__ */ new Date()).toISOString(), delivery, payload });
|
|
1835
|
+
(0, eventLog_1.appendEventLog)(logFile, { loggedAt: (/* @__PURE__ */ new Date()).toISOString(), delivery, payload, ...outbox ? { outbox } : {} });
|
|
1025
1836
|
}
|
|
1837
|
+
/** Never throws: a renewal that errors is a failed renewal, not a failed turn. */
|
|
1026
1838
|
async renewEventToken() {
|
|
1027
1839
|
try {
|
|
1028
1840
|
const renewed = await (0, http_1.renewToolToken)(this.config.apiBaseUrl, this.eventWriteToken, this.signal());
|
|
@@ -1040,6 +1852,9 @@ var require_eventSender = __commonJS({
|
|
|
1040
1852
|
}
|
|
1041
1853
|
};
|
|
1042
1854
|
exports.AscendaEventSender = AscendaEventSender2;
|
|
1855
|
+
function withNote(detail, note) {
|
|
1856
|
+
return detail ? `${detail} (${note})` : note;
|
|
1857
|
+
}
|
|
1043
1858
|
}
|
|
1044
1859
|
});
|
|
1045
1860
|
|
|
@@ -1118,10 +1933,10 @@ var require_contextRegistry = __commonJS({
|
|
|
1118
1933
|
}
|
|
1119
1934
|
return upsert(updates, options);
|
|
1120
1935
|
}
|
|
1121
|
-
function recordWorkContextAlias(
|
|
1122
|
-
if (!
|
|
1936
|
+
function recordWorkContextAlias(hash, label, observedPath, options) {
|
|
1937
|
+
if (!hash || !label)
|
|
1123
1938
|
return false;
|
|
1124
|
-
return upsert([{ hash
|
|
1939
|
+
return upsert([{ hash, kind: "alias", label, observedPath: observedPath ?? null }], options);
|
|
1125
1940
|
}
|
|
1126
1941
|
function upsert(updates, options) {
|
|
1127
1942
|
if (updates.length === 0)
|
|
@@ -1162,28 +1977,291 @@ var require_contextRegistry = __commonJS({
|
|
|
1162
1977
|
dirty = true;
|
|
1163
1978
|
}
|
|
1164
1979
|
}
|
|
1165
|
-
if (!dirty)
|
|
1980
|
+
if (!dirty)
|
|
1981
|
+
return false;
|
|
1982
|
+
writeRegistry(registryFilePath, registry);
|
|
1983
|
+
return true;
|
|
1984
|
+
} catch {
|
|
1985
|
+
return false;
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
function dayOf(iso) {
|
|
1989
|
+
return iso.slice(0, 10);
|
|
1990
|
+
}
|
|
1991
|
+
function writeRegistry(registryFilePath, registry) {
|
|
1992
|
+
const dir = path.dirname(registryFilePath);
|
|
1993
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1994
|
+
const tmp = `${registryFilePath}.${process.pid}.tmp`;
|
|
1995
|
+
fs.writeFileSync(tmp, `${JSON.stringify(registry, null, 2)}
|
|
1996
|
+
`, { encoding: "utf8", mode: 384 });
|
|
1997
|
+
fs.renameSync(tmp, registryFilePath);
|
|
1998
|
+
if (process.platform !== "win32") {
|
|
1999
|
+
fs.chmodSync(registryFilePath, 384);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
});
|
|
2004
|
+
|
|
2005
|
+
// ../packages/tool-kit/out/credentials.js
|
|
2006
|
+
var require_credentials = __commonJS({
|
|
2007
|
+
"../packages/tool-kit/out/credentials.js"(exports) {
|
|
2008
|
+
"use strict";
|
|
2009
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
2010
|
+
if (k2 === void 0) k2 = k;
|
|
2011
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
2012
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
2013
|
+
desc = { enumerable: true, get: function() {
|
|
2014
|
+
return m[k];
|
|
2015
|
+
} };
|
|
2016
|
+
}
|
|
2017
|
+
Object.defineProperty(o, k2, desc);
|
|
2018
|
+
} : function(o, m, k, k2) {
|
|
2019
|
+
if (k2 === void 0) k2 = k;
|
|
2020
|
+
o[k2] = m[k];
|
|
2021
|
+
});
|
|
2022
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
2023
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
2024
|
+
} : function(o, v) {
|
|
2025
|
+
o["default"] = v;
|
|
2026
|
+
});
|
|
2027
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
2028
|
+
var ownKeys = function(o) {
|
|
2029
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
2030
|
+
var ar = [];
|
|
2031
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
2032
|
+
return ar;
|
|
2033
|
+
};
|
|
2034
|
+
return ownKeys(o);
|
|
2035
|
+
};
|
|
2036
|
+
return function(mod) {
|
|
2037
|
+
if (mod && mod.__esModule) return mod;
|
|
2038
|
+
var result = {};
|
|
2039
|
+
if (mod != null) {
|
|
2040
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
2041
|
+
}
|
|
2042
|
+
__setModuleDefault(result, mod);
|
|
2043
|
+
return result;
|
|
2044
|
+
};
|
|
2045
|
+
}();
|
|
2046
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2047
|
+
exports.credentialsFilePath = credentialsFilePath;
|
|
2048
|
+
exports.readMachineCredentials = readMachineCredentials;
|
|
2049
|
+
exports.writeMachineCredentials = writeMachineCredentials;
|
|
2050
|
+
exports.writeTopLevelCredentials = writeTopLevelCredentials;
|
|
2051
|
+
exports.readHostCredentials = readHostCredentials;
|
|
2052
|
+
exports.writeHostCredentials = writeHostCredentials;
|
|
2053
|
+
exports.removeHostCredentials = removeHostCredentials;
|
|
2054
|
+
var fs = __importStar(__require("fs"));
|
|
2055
|
+
var path = __importStar(__require("path"));
|
|
2056
|
+
var tokenStore_1 = require_tokenStore();
|
|
2057
|
+
function credentialsFilePath() {
|
|
2058
|
+
return path.join((0, tokenStore_1.ascendaHome)(), "credentials.json");
|
|
2059
|
+
}
|
|
2060
|
+
function readMachineCredentials() {
|
|
2061
|
+
try {
|
|
2062
|
+
const raw = fs.readFileSync(credentialsFilePath(), "utf8").trim();
|
|
2063
|
+
if (!raw)
|
|
2064
|
+
return void 0;
|
|
2065
|
+
const parsed = JSON.parse(raw);
|
|
2066
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
2067
|
+
return void 0;
|
|
2068
|
+
return parsed;
|
|
2069
|
+
} catch {
|
|
2070
|
+
return void 0;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
function writeMachineCredentials(credentials) {
|
|
2074
|
+
const file = credentialsFilePath();
|
|
2075
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 448 });
|
|
2076
|
+
fs.writeFileSync(file, `${JSON.stringify(credentials, null, 2)}
|
|
2077
|
+
`, { encoding: "utf8", mode: 384 });
|
|
2078
|
+
if (process.platform !== "win32") {
|
|
2079
|
+
fs.chmodSync(path.dirname(file), 448);
|
|
2080
|
+
fs.chmodSync(file, 384);
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
function writeTopLevelCredentials(credentials) {
|
|
2084
|
+
const existing = readMachineCredentials();
|
|
2085
|
+
writeMachineCredentials({ ...credentials, ...existing?.tools ? { tools: existing.tools } : {} });
|
|
2086
|
+
}
|
|
2087
|
+
function readHostCredentials(host) {
|
|
2088
|
+
const entry = readMachineCredentials()?.tools?.[host];
|
|
2089
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
2090
|
+
return void 0;
|
|
2091
|
+
return entry;
|
|
2092
|
+
}
|
|
2093
|
+
function writeHostCredentials(host, credentials) {
|
|
2094
|
+
const existing = readMachineCredentials() ?? {};
|
|
2095
|
+
writeMachineCredentials({ ...existing, tools: { ...existing.tools ?? {}, [host]: credentials } });
|
|
2096
|
+
}
|
|
2097
|
+
function removeHostCredentials(host) {
|
|
2098
|
+
const existing = readMachineCredentials();
|
|
2099
|
+
if (!existing?.tools || !(host in existing.tools))
|
|
2100
|
+
return;
|
|
2101
|
+
const tools = { ...existing.tools };
|
|
2102
|
+
delete tools[host];
|
|
2103
|
+
const next = { ...existing };
|
|
2104
|
+
if (Object.keys(tools).length)
|
|
2105
|
+
next.tools = tools;
|
|
2106
|
+
else
|
|
2107
|
+
delete next.tools;
|
|
2108
|
+
writeMachineCredentials(next);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
});
|
|
2112
|
+
|
|
2113
|
+
// ../packages/tool-kit/out/forgeProject.js
|
|
2114
|
+
var require_forgeProject = __commonJS({
|
|
2115
|
+
"../packages/tool-kit/out/forgeProject.js"(exports) {
|
|
2116
|
+
"use strict";
|
|
2117
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
2118
|
+
if (k2 === void 0) k2 = k;
|
|
2119
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
2120
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
2121
|
+
desc = { enumerable: true, get: function() {
|
|
2122
|
+
return m[k];
|
|
2123
|
+
} };
|
|
2124
|
+
}
|
|
2125
|
+
Object.defineProperty(o, k2, desc);
|
|
2126
|
+
} : function(o, m, k, k2) {
|
|
2127
|
+
if (k2 === void 0) k2 = k;
|
|
2128
|
+
o[k2] = m[k];
|
|
2129
|
+
});
|
|
2130
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
2131
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
2132
|
+
} : function(o, v) {
|
|
2133
|
+
o["default"] = v;
|
|
2134
|
+
});
|
|
2135
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
2136
|
+
var ownKeys = function(o) {
|
|
2137
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
2138
|
+
var ar = [];
|
|
2139
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
2140
|
+
return ar;
|
|
2141
|
+
};
|
|
2142
|
+
return ownKeys(o);
|
|
2143
|
+
};
|
|
2144
|
+
return function(mod) {
|
|
2145
|
+
if (mod && mod.__esModule) return mod;
|
|
2146
|
+
var result = {};
|
|
2147
|
+
if (mod != null) {
|
|
2148
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
2149
|
+
}
|
|
2150
|
+
__setModuleDefault(result, mod);
|
|
2151
|
+
return result;
|
|
2152
|
+
};
|
|
2153
|
+
}();
|
|
2154
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2155
|
+
exports.forgeProjectHash = forgeProjectHash2;
|
|
2156
|
+
exports.parseForgeFullName = parseForgeFullName;
|
|
2157
|
+
exports.readForgeFullName = readForgeFullName;
|
|
2158
|
+
exports.forgeFullNameFromConfig = forgeFullNameFromConfig;
|
|
2159
|
+
exports.recordForgeProjectAlias = recordForgeProjectAlias;
|
|
2160
|
+
var fs = __importStar(__require("fs"));
|
|
2161
|
+
var path = __importStar(__require("path"));
|
|
2162
|
+
var contextRegistry_1 = require_contextRegistry();
|
|
2163
|
+
function forgeProjectHash2(value) {
|
|
2164
|
+
let h = 2166136261;
|
|
2165
|
+
for (let i = 0; i < value.length; i++) {
|
|
2166
|
+
h ^= value.charCodeAt(i);
|
|
2167
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
2168
|
+
}
|
|
2169
|
+
return h.toString(16).padStart(8, "0");
|
|
2170
|
+
}
|
|
2171
|
+
function parseForgeFullName(remoteUrl) {
|
|
2172
|
+
if (!remoteUrl)
|
|
2173
|
+
return null;
|
|
2174
|
+
const trimmed = remoteUrl.trim();
|
|
2175
|
+
if (!trimmed)
|
|
2176
|
+
return null;
|
|
2177
|
+
const scp = /^(?:[^@/]+@)?([^/:]+):(.+)$/.exec(trimmed);
|
|
2178
|
+
const scheme = /^([a-z][a-z0-9+.-]*):\/\/(?:[^@/]*@)?([^/:]+)(?::\d+)?\/(.+)$/i.exec(trimmed);
|
|
2179
|
+
let host;
|
|
2180
|
+
let repoPath;
|
|
2181
|
+
if (scheme) {
|
|
2182
|
+
host = scheme[2];
|
|
2183
|
+
repoPath = scheme[3];
|
|
2184
|
+
} else if (scp && !/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
|
|
2185
|
+
host = scp[1];
|
|
2186
|
+
repoPath = scp[2];
|
|
2187
|
+
} else {
|
|
2188
|
+
return null;
|
|
2189
|
+
}
|
|
2190
|
+
const normalizedHost = host.toLowerCase().replace(/^www\./, "");
|
|
2191
|
+
if (normalizedHost !== "github.com")
|
|
2192
|
+
return null;
|
|
2193
|
+
const segments = repoPath.split("/").filter((segment) => segment.length > 0);
|
|
2194
|
+
if (segments.length < 2)
|
|
2195
|
+
return null;
|
|
2196
|
+
const owner = segments[0];
|
|
2197
|
+
const repo = segments[1].replace(/\.git$/, "");
|
|
2198
|
+
if (!owner || !repo)
|
|
2199
|
+
return null;
|
|
2200
|
+
return `${owner}/${repo}`;
|
|
2201
|
+
}
|
|
2202
|
+
function readForgeFullName(repositoryRoot) {
|
|
2203
|
+
if (!repositoryRoot)
|
|
2204
|
+
return null;
|
|
2205
|
+
let config;
|
|
2206
|
+
try {
|
|
2207
|
+
config = fs.readFileSync(path.join(repositoryRoot, ".git", "config"), "utf8");
|
|
2208
|
+
} catch {
|
|
2209
|
+
return null;
|
|
2210
|
+
}
|
|
2211
|
+
return forgeFullNameFromConfig(config);
|
|
2212
|
+
}
|
|
2213
|
+
function forgeFullNameFromConfig(config) {
|
|
2214
|
+
const remotes = /* @__PURE__ */ new Map();
|
|
2215
|
+
let currentRemote = null;
|
|
2216
|
+
for (const rawLine of config.split(/\r?\n/)) {
|
|
2217
|
+
const line = rawLine.trim();
|
|
2218
|
+
if (!line || line.startsWith("#") || line.startsWith(";"))
|
|
2219
|
+
continue;
|
|
2220
|
+
const section = /^\[([^\]]*)\]$/.exec(line);
|
|
2221
|
+
if (section) {
|
|
2222
|
+
const remote = /^remote\s+"(.*)"$/.exec(section[1].trim());
|
|
2223
|
+
currentRemote = remote ? remote[1] : null;
|
|
2224
|
+
continue;
|
|
2225
|
+
}
|
|
2226
|
+
if (!currentRemote)
|
|
2227
|
+
continue;
|
|
2228
|
+
const entry = /^url\s*=\s*(.*)$/.exec(line);
|
|
2229
|
+
if (entry && !remotes.has(currentRemote))
|
|
2230
|
+
remotes.set(currentRemote, entry[1].trim());
|
|
2231
|
+
}
|
|
2232
|
+
const ordered = [
|
|
2233
|
+
...remotes.has("origin") ? ["origin"] : [],
|
|
2234
|
+
...remotes.has("upstream") ? ["upstream"] : [],
|
|
2235
|
+
...[...remotes.keys()].filter((name) => name !== "origin" && name !== "upstream")
|
|
2236
|
+
];
|
|
2237
|
+
for (const name of ordered) {
|
|
2238
|
+
const fullName = parseForgeFullName(remotes.get(name));
|
|
2239
|
+
if (fullName)
|
|
2240
|
+
return fullName;
|
|
2241
|
+
}
|
|
2242
|
+
return null;
|
|
2243
|
+
}
|
|
2244
|
+
function recordForgeProjectAlias(context, options) {
|
|
2245
|
+
try {
|
|
2246
|
+
if (!context?.projectHash || !context.projectLabel || !context.projectPath)
|
|
1166
2247
|
return false;
|
|
1167
|
-
|
|
1168
|
-
|
|
2248
|
+
const fullName = readForgeFullName(context.projectPath);
|
|
2249
|
+
if (!fullName)
|
|
2250
|
+
return false;
|
|
2251
|
+
const variants = [fullName, fullName.toLowerCase()].filter((value, index, all) => all.indexOf(value) === index);
|
|
2252
|
+
let wrote = false;
|
|
2253
|
+
for (const variant of variants) {
|
|
2254
|
+
const hash = forgeProjectHash2(variant);
|
|
2255
|
+
if (hash === context.projectHash || hash === context.workspaceHash)
|
|
2256
|
+
continue;
|
|
2257
|
+
if ((0, contextRegistry_1.recordWorkContextAlias)(hash, context.projectLabel, context.projectPath, options))
|
|
2258
|
+
wrote = true;
|
|
2259
|
+
}
|
|
2260
|
+
return wrote;
|
|
1169
2261
|
} catch {
|
|
1170
2262
|
return false;
|
|
1171
2263
|
}
|
|
1172
2264
|
}
|
|
1173
|
-
function dayOf(iso) {
|
|
1174
|
-
return iso.slice(0, 10);
|
|
1175
|
-
}
|
|
1176
|
-
function writeRegistry(registryFilePath, registry) {
|
|
1177
|
-
const dir = path.dirname(registryFilePath);
|
|
1178
|
-
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1179
|
-
const tmp = `${registryFilePath}.${process.pid}.tmp`;
|
|
1180
|
-
fs.writeFileSync(tmp, `${JSON.stringify(registry, null, 2)}
|
|
1181
|
-
`, { encoding: "utf8", mode: 384 });
|
|
1182
|
-
fs.renameSync(tmp, registryFilePath);
|
|
1183
|
-
if (process.platform !== "win32") {
|
|
1184
|
-
fs.chmodSync(registryFilePath, 384);
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
2265
|
}
|
|
1188
2266
|
});
|
|
1189
2267
|
|
|
@@ -1313,6 +2391,10 @@ var require_workContext = __commonJS({
|
|
|
1313
2391
|
}();
|
|
1314
2392
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1315
2393
|
exports.deriveWorkContext = deriveWorkContext;
|
|
2394
|
+
exports.normalizeBranchName = normalizeBranchName;
|
|
2395
|
+
exports.deriveBranchHash = deriveBranchHash;
|
|
2396
|
+
exports.readBranchName = readBranchName;
|
|
2397
|
+
exports.deriveBranchHashForCwd = deriveBranchHashForCwd;
|
|
1316
2398
|
var fs = __importStar(__require("fs"));
|
|
1317
2399
|
var path = __importStar(__require("path"));
|
|
1318
2400
|
var salt_1 = require_salt();
|
|
@@ -1327,6 +2409,8 @@ var require_workContext = __commonJS({
|
|
|
1327
2409
|
} catch {
|
|
1328
2410
|
roots = null;
|
|
1329
2411
|
}
|
|
2412
|
+
if (!roots)
|
|
2413
|
+
roots = inferRootsFromPath(startPath);
|
|
1330
2414
|
const workspacePath = roots?.checkoutRoot ?? startPath;
|
|
1331
2415
|
const workspaceLabel = basenameOf(workspacePath);
|
|
1332
2416
|
if (!workspaceLabel)
|
|
@@ -1355,9 +2439,12 @@ var require_workContext = __commonJS({
|
|
|
1355
2439
|
stat = null;
|
|
1356
2440
|
}
|
|
1357
2441
|
if (stat?.isDirectory())
|
|
1358
|
-
return { checkoutRoot: dir, canonicalRoot: dir };
|
|
1359
|
-
if (stat?.isFile())
|
|
1360
|
-
|
|
2442
|
+
return { checkoutRoot: dir, canonicalRoot: dir, gitDir: dotGit };
|
|
2443
|
+
if (stat?.isFile()) {
|
|
2444
|
+
const gitDir = readGitdirPointer(dotGit, dir);
|
|
2445
|
+
const canonicalRoot = (gitDir ? worktreeParentRoot(gitDir) : null) ?? dir;
|
|
2446
|
+
return { checkoutRoot: dir, canonicalRoot, gitDir };
|
|
2447
|
+
}
|
|
1361
2448
|
const parent = path.dirname(dir);
|
|
1362
2449
|
if (parent === dir)
|
|
1363
2450
|
return null;
|
|
@@ -1365,22 +2452,45 @@ var require_workContext = __commonJS({
|
|
|
1365
2452
|
}
|
|
1366
2453
|
return null;
|
|
1367
2454
|
}
|
|
1368
|
-
function
|
|
1369
|
-
let gitdir;
|
|
2455
|
+
function readGitdirPointer(dotGitFile, containingDir) {
|
|
1370
2456
|
try {
|
|
1371
2457
|
const match = /^gitdir:\s*(.+)\s*$/m.exec(fs.readFileSync(dotGitFile, "utf8"));
|
|
1372
2458
|
if (!match)
|
|
1373
2459
|
return null;
|
|
1374
|
-
|
|
2460
|
+
return path.resolve(containingDir, match[1].trim());
|
|
1375
2461
|
} catch {
|
|
1376
2462
|
return null;
|
|
1377
2463
|
}
|
|
1378
|
-
|
|
2464
|
+
}
|
|
2465
|
+
function worktreeParentRoot(resolvedGitDir) {
|
|
1379
2466
|
const marker = `${path.sep}.git${path.sep}worktrees${path.sep}`;
|
|
1380
|
-
const idx =
|
|
2467
|
+
const idx = resolvedGitDir.indexOf(marker);
|
|
1381
2468
|
if (idx === -1)
|
|
1382
2469
|
return null;
|
|
1383
|
-
return
|
|
2470
|
+
return resolvedGitDir.slice(0, idx);
|
|
2471
|
+
}
|
|
2472
|
+
function inferRootsFromPath(startPath) {
|
|
2473
|
+
const sep = startPath.includes("\\") && !startPath.includes("/") ? "\\" : "/";
|
|
2474
|
+
const leading = /^[\\/]/.test(startPath) ? sep : "";
|
|
2475
|
+
const segments = startPath.split(/[\\/]/).filter(Boolean);
|
|
2476
|
+
const join = (count) => leading + segments.slice(0, count).join(sep);
|
|
2477
|
+
for (let i = 0; i + 2 < segments.length; i++) {
|
|
2478
|
+
if (segments[i] === ".claude" && segments[i + 1] === "worktrees") {
|
|
2479
|
+
if (i === 0)
|
|
2480
|
+
return null;
|
|
2481
|
+
return { checkoutRoot: join(i + 3), canonicalRoot: join(i), gitDir: null };
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
for (let i = 0; i + 1 < segments.length; i++) {
|
|
2485
|
+
const folder = segments[i];
|
|
2486
|
+
const suffix = ["-worktrees", "-wt"].find((s) => folder.endsWith(s) && folder.length > s.length);
|
|
2487
|
+
if (!suffix)
|
|
2488
|
+
continue;
|
|
2489
|
+
const repoName = folder.slice(0, -suffix.length);
|
|
2490
|
+
const canonicalRoot = leading + [...segments.slice(0, i), repoName].join(sep);
|
|
2491
|
+
return { checkoutRoot: join(i + 2), canonicalRoot, gitDir: null };
|
|
2492
|
+
}
|
|
2493
|
+
return null;
|
|
1384
2494
|
}
|
|
1385
2495
|
function stripTrailingSeparators(value) {
|
|
1386
2496
|
let end = value.length;
|
|
@@ -1392,6 +2502,55 @@ var require_workContext = __commonJS({
|
|
|
1392
2502
|
const segment = value.split(/[\\/]/).filter(Boolean).pop() ?? null;
|
|
1393
2503
|
return segment && segment.length > 0 ? segment : null;
|
|
1394
2504
|
}
|
|
2505
|
+
var REFS_HEADS_PREFIX = "refs/heads/";
|
|
2506
|
+
function normalizeBranchName(branch) {
|
|
2507
|
+
if (!branch)
|
|
2508
|
+
return null;
|
|
2509
|
+
let name = branch.trim();
|
|
2510
|
+
if (name.startsWith(REFS_HEADS_PREFIX))
|
|
2511
|
+
name = name.slice(REFS_HEADS_PREFIX.length).trim();
|
|
2512
|
+
if (!name || name === "HEAD")
|
|
2513
|
+
return null;
|
|
2514
|
+
return name;
|
|
2515
|
+
}
|
|
2516
|
+
function deriveBranchHash(branch, saltFilePath) {
|
|
2517
|
+
const name = normalizeBranchName(branch);
|
|
2518
|
+
if (!name)
|
|
2519
|
+
return null;
|
|
2520
|
+
try {
|
|
2521
|
+
return (0, salt_1.hashWithMachineSalt)(name, saltFilePath);
|
|
2522
|
+
} catch {
|
|
2523
|
+
return null;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
function readBranchName(cwd) {
|
|
2527
|
+
if (!cwd || !cwd.trim())
|
|
2528
|
+
return null;
|
|
2529
|
+
let gitDir = null;
|
|
2530
|
+
try {
|
|
2531
|
+
gitDir = resolveRepositoryRoots(stripTrailingSeparators(cwd.trim()))?.gitDir ?? null;
|
|
2532
|
+
} catch {
|
|
2533
|
+
gitDir = null;
|
|
2534
|
+
}
|
|
2535
|
+
if (!gitDir)
|
|
2536
|
+
return null;
|
|
2537
|
+
let head;
|
|
2538
|
+
try {
|
|
2539
|
+
head = fs.readFileSync(path.join(gitDir, "HEAD"), "utf8").trim();
|
|
2540
|
+
} catch {
|
|
2541
|
+
return null;
|
|
2542
|
+
}
|
|
2543
|
+
const match = /^ref:\s*(.+)$/.exec(head);
|
|
2544
|
+
if (!match)
|
|
2545
|
+
return null;
|
|
2546
|
+
const ref = match[1].trim();
|
|
2547
|
+
if (!ref.startsWith(REFS_HEADS_PREFIX))
|
|
2548
|
+
return null;
|
|
2549
|
+
return normalizeBranchName(ref);
|
|
2550
|
+
}
|
|
2551
|
+
function deriveBranchHashForCwd(cwd, saltFilePath) {
|
|
2552
|
+
return deriveBranchHash(readBranchName(cwd), saltFilePath);
|
|
2553
|
+
}
|
|
1395
2554
|
}
|
|
1396
2555
|
});
|
|
1397
2556
|
|
|
@@ -1400,35 +2559,69 @@ var require_hookAdapter = __commonJS({
|
|
|
1400
2559
|
"../packages/tool-kit/out/hookAdapter.js"(exports) {
|
|
1401
2560
|
"use strict";
|
|
1402
2561
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1403
|
-
exports.DEFAULT_API_BASE_URL = void 0;
|
|
2562
|
+
exports.MissingInstallationIdError = exports.DEFAULT_API_BASE_URL = void 0;
|
|
2563
|
+
exports.resolveCliAgentInstallationId = resolveCliAgentInstallationId;
|
|
1404
2564
|
exports.resolveContextHashes = resolveContextHashes;
|
|
1405
2565
|
exports.loadCliAgentConfig = loadCliAgentConfig;
|
|
1406
2566
|
exports.deliverHookEvents = deliverHookEvents;
|
|
1407
2567
|
var contextRegistry_1 = require_contextRegistry();
|
|
2568
|
+
var credentials_1 = require_credentials();
|
|
2569
|
+
var forgeProject_1 = require_forgeProject();
|
|
1408
2570
|
var eventLog_1 = require_eventLog();
|
|
1409
2571
|
var eventSender_1 = require_eventSender();
|
|
2572
|
+
var stateStore_1 = require_stateStore();
|
|
1410
2573
|
var tokenStore_1 = require_tokenStore();
|
|
1411
2574
|
var workContext_1 = require_workContext();
|
|
1412
2575
|
exports.DEFAULT_API_BASE_URL = "https://api.ascenda.one";
|
|
2576
|
+
var MissingInstallationIdError = class extends Error {
|
|
2577
|
+
/** The token files that were considered — none, or too many to pick from. */
|
|
2578
|
+
candidates;
|
|
2579
|
+
toolType;
|
|
2580
|
+
constructor(toolType, candidates, setupCommand) {
|
|
2581
|
+
super(candidates.length === 0 ? `Not configured: no ASCENDA_TOOL_INSTALLATION_ID, no pairing in ~/.ascenda/credentials.json, and no ${toolType} token in ~/.ascenda/tokens/. Run: ${setupCommand}` : `Not configured: no ASCENDA_TOOL_INSTALLATION_ID, no pairing in ~/.ascenda/credentials.json, and ${candidates.length} ${toolType} tokens in ~/.ascenda/tokens/ (${candidates.join(", ")}) \u2014 refusing to guess. Export ASCENDA_TOOL_INSTALLATION_ID to choose one, or run: ${setupCommand}`);
|
|
2582
|
+
this.name = "MissingInstallationIdError";
|
|
2583
|
+
this.toolType = toolType;
|
|
2584
|
+
this.candidates = candidates;
|
|
2585
|
+
}
|
|
2586
|
+
};
|
|
2587
|
+
exports.MissingInstallationIdError = MissingInstallationIdError;
|
|
2588
|
+
function resolveCliAgentInstallationId(toolType, identity = {}) {
|
|
2589
|
+
const fromEnv = process.env.ASCENDA_TOOL_INSTALLATION_ID?.trim();
|
|
2590
|
+
if (fromEnv)
|
|
2591
|
+
return { toolInstallationId: qualify(toolType, fromEnv), source: "env" };
|
|
2592
|
+
const fromCredentials = identity.host ? (0, credentials_1.readHostCredentials)(identity.host)?.toolInstallationId?.trim() : void 0;
|
|
2593
|
+
if (fromCredentials)
|
|
2594
|
+
return { toolInstallationId: qualify(toolType, fromCredentials), source: "credentials" };
|
|
2595
|
+
const candidates = (0, tokenStore_1.listPersistedToolInstallationIds)(toolType);
|
|
2596
|
+
if (candidates.length === 1)
|
|
2597
|
+
return { toolInstallationId: candidates[0], source: "disk" };
|
|
2598
|
+
throw new MissingInstallationIdError(toolType, candidates, identity.setupCommand ?? defaultSetupCommand(identity.host));
|
|
2599
|
+
}
|
|
2600
|
+
function defaultSetupCommand(host) {
|
|
2601
|
+
return host ? `npx @ascenda-one/${host.replace(/_cli$/, "")}-hooks setup` : "the agent's setup command";
|
|
2602
|
+
}
|
|
2603
|
+
function qualify(toolType, value) {
|
|
2604
|
+
return value.includes(":") ? value : `${toolType}:${value}`;
|
|
2605
|
+
}
|
|
1413
2606
|
function resolveContextHashes(cwd) {
|
|
1414
2607
|
const workspaceOverride = process.env.ASCENDA_WORKSPACE_HASH?.trim() || null;
|
|
1415
2608
|
const projectOverride = process.env.ASCENDA_PROJECT_HASH?.trim() || null;
|
|
1416
2609
|
if (workspaceOverride && projectOverride)
|
|
1417
2610
|
return { workspaceHash: workspaceOverride, projectHash: projectOverride };
|
|
1418
2611
|
const context = (0, workContext_1.deriveWorkContext)(cwd ?? process.cwd());
|
|
1419
|
-
if (context)
|
|
2612
|
+
if (context) {
|
|
1420
2613
|
(0, contextRegistry_1.recordWorkContext)(context);
|
|
2614
|
+
(0, forgeProject_1.recordForgeProjectAlias)(context);
|
|
2615
|
+
}
|
|
1421
2616
|
return {
|
|
1422
2617
|
workspaceHash: workspaceOverride ?? context?.workspaceHash ?? null,
|
|
1423
2618
|
projectHash: projectOverride ?? context?.projectHash ?? null
|
|
1424
2619
|
};
|
|
1425
2620
|
}
|
|
1426
|
-
function loadCliAgentConfig(toolType, sessionIdFromHook, cwd) {
|
|
1427
|
-
const
|
|
1428
|
-
const
|
|
1429
|
-
|
|
1430
|
-
throw new Error("Missing ASCENDA_TOOL_INSTALLATION_ID");
|
|
1431
|
-
const toolInstallationId = toolInstallationIdRaw.trim().includes(":") ? toolInstallationIdRaw.trim() : `${toolType}:${toolInstallationIdRaw.trim()}`;
|
|
2621
|
+
function loadCliAgentConfig(toolType, sessionIdFromHook, cwd, identity = {}) {
|
|
2622
|
+
const credentials = identity.host ? (0, credentials_1.readHostCredentials)(identity.host) : void 0;
|
|
2623
|
+
const apiBaseUrl = (process.env.ASCENDA_API_BASE_URL ?? credentials?.apiBaseUrl ?? exports.DEFAULT_API_BASE_URL).replace(/\/$/, "");
|
|
2624
|
+
const { toolInstallationId } = resolveCliAgentInstallationId(toolType, identity);
|
|
1432
2625
|
const tokenFilePath = process.env.ASCENDA_EVENT_WRITE_TOKEN_FILE ?? (0, tokenStore_1.defaultTokenFilePath)(toolInstallationId);
|
|
1433
2626
|
const fileToken = (0, tokenStore_1.readTokenFile)(tokenFilePath);
|
|
1434
2627
|
const eventWriteToken = fileToken ?? process.env.ASCENDA_EVENT_WRITE_TOKEN;
|
|
@@ -1455,8 +2648,10 @@ var require_hookAdapter = __commonJS({
|
|
|
1455
2648
|
const notice = options.onNotice ?? ((message) => console.error(message));
|
|
1456
2649
|
let config;
|
|
1457
2650
|
try {
|
|
1458
|
-
config = loadCliAgentConfig(options.toolType, options.sessionId, options.cwd);
|
|
2651
|
+
config = loadCliAgentConfig(options.toolType, options.sessionId, options.cwd, options);
|
|
1459
2652
|
} catch (error) {
|
|
2653
|
+
if (error instanceof MissingInstallationIdError)
|
|
2654
|
+
journalSkippedSend(options.host, error);
|
|
1460
2655
|
const logFile = (0, eventLog_1.resolveEventLogPath)();
|
|
1461
2656
|
if (!logFile)
|
|
1462
2657
|
throw error;
|
|
@@ -1496,13 +2691,19 @@ var require_hookAdapter = __commonJS({
|
|
|
1496
2691
|
} else if (result === "auth_failed") {
|
|
1497
2692
|
notice("Ascenda telemetry paused: connection revoked or expired. Re-pair via an Ascenda IDE extension or pairing-sim.");
|
|
1498
2693
|
} else if (result === "transport_error") {
|
|
1499
|
-
notice("Ascenda telemetry paused: the ingest endpoint could not be reached. Your work is unaffected.");
|
|
2694
|
+
notice("Ascenda telemetry paused: the ingest endpoint could not be reached; the event is kept in the outbox. Your work is unaffected.");
|
|
1500
2695
|
} else {
|
|
1501
2696
|
notice(`Ascenda telemetry rejected: ${result}`);
|
|
1502
2697
|
}
|
|
1503
2698
|
return;
|
|
1504
2699
|
}
|
|
1505
2700
|
}
|
|
2701
|
+
function journalSkippedSend(host, error) {
|
|
2702
|
+
const who = host ? `${host}: ` : "";
|
|
2703
|
+
(0, stateStore_1.recordSendOutcome)((0, stateStore_1.unresolvedStateFilePath)(error.toolType), (0, stateStore_1.unresolvedToolInstallationId)(error.toolType), "skipped_no_installation_id", {
|
|
2704
|
+
detail: error.candidates.length === 0 ? `${who}no ASCENDA_TOOL_INSTALLATION_ID, no credentials.json pairing, no ${error.toolType} token file` : `${who}no ASCENDA_TOOL_INSTALLATION_ID, no credentials.json pairing, ${error.candidates.length} ${error.toolType} token files (${error.candidates.join(", ")})`
|
|
2705
|
+
});
|
|
2706
|
+
}
|
|
1506
2707
|
function parsePositiveInt(value) {
|
|
1507
2708
|
const n = Number(value);
|
|
1508
2709
|
return Number.isInteger(n) && n > 0 ? n : void 0;
|
|
@@ -1510,6 +2711,387 @@ var require_hookAdapter = __commonJS({
|
|
|
1510
2711
|
}
|
|
1511
2712
|
});
|
|
1512
2713
|
|
|
2714
|
+
// ../packages/tool-kit/out/cliAgentSetup.js
|
|
2715
|
+
var require_cliAgentSetup = __commonJS({
|
|
2716
|
+
"../packages/tool-kit/out/cliAgentSetup.js"(exports) {
|
|
2717
|
+
"use strict";
|
|
2718
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
2719
|
+
if (k2 === void 0) k2 = k;
|
|
2720
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
2721
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
2722
|
+
desc = { enumerable: true, get: function() {
|
|
2723
|
+
return m[k];
|
|
2724
|
+
} };
|
|
2725
|
+
}
|
|
2726
|
+
Object.defineProperty(o, k2, desc);
|
|
2727
|
+
} : function(o, m, k, k2) {
|
|
2728
|
+
if (k2 === void 0) k2 = k;
|
|
2729
|
+
o[k2] = m[k];
|
|
2730
|
+
});
|
|
2731
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
2732
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
2733
|
+
} : function(o, v) {
|
|
2734
|
+
o["default"] = v;
|
|
2735
|
+
});
|
|
2736
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
2737
|
+
var ownKeys = function(o) {
|
|
2738
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
2739
|
+
var ar = [];
|
|
2740
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
2741
|
+
return ar;
|
|
2742
|
+
};
|
|
2743
|
+
return ownKeys(o);
|
|
2744
|
+
};
|
|
2745
|
+
return function(mod) {
|
|
2746
|
+
if (mod && mod.__esModule) return mod;
|
|
2747
|
+
var result = {};
|
|
2748
|
+
if (mod != null) {
|
|
2749
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
2750
|
+
}
|
|
2751
|
+
__setModuleDefault(result, mod);
|
|
2752
|
+
return result;
|
|
2753
|
+
};
|
|
2754
|
+
}();
|
|
2755
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2756
|
+
exports.isCliAgentManagementCommand = isCliAgentManagementCommand;
|
|
2757
|
+
exports.cliAgentHookBinPath = cliAgentHookBinPath;
|
|
2758
|
+
exports.runCliAgentSetup = runCliAgentSetup;
|
|
2759
|
+
exports.writeHookSettings = writeHookSettings;
|
|
2760
|
+
exports.findStaleHookCommands = findStaleHookCommands;
|
|
2761
|
+
var crypto = __importStar(__require("crypto"));
|
|
2762
|
+
var fs = __importStar(__require("fs"));
|
|
2763
|
+
var os = __importStar(__require("os"));
|
|
2764
|
+
var path = __importStar(__require("path"));
|
|
2765
|
+
var credentials_1 = require_credentials();
|
|
2766
|
+
var hookAdapter_1 = require_hookAdapter();
|
|
2767
|
+
var http_1 = require_http();
|
|
2768
|
+
var tokenStore_1 = require_tokenStore();
|
|
2769
|
+
var MANAGEMENT_COMMANDS = /* @__PURE__ */ new Set(["setup", "install", "status", "uninstall", "-h", "--help"]);
|
|
2770
|
+
function isCliAgentManagementCommand(argument) {
|
|
2771
|
+
return argument !== void 0 && MANAGEMENT_COMMANDS.has(argument);
|
|
2772
|
+
}
|
|
2773
|
+
function cliAgentHookBinPath(binaryName) {
|
|
2774
|
+
return path.join((0, tokenStore_1.ascendaHome)(), "bin", binaryName);
|
|
2775
|
+
}
|
|
2776
|
+
function usage(spec) {
|
|
2777
|
+
return `${spec.binaryName} setup \u2014 wire ${spec.displayName} to Ascenda telemetry
|
|
2778
|
+
|
|
2779
|
+
npx ${spec.packageName} setup [options]
|
|
2780
|
+
npx ${spec.packageName} status
|
|
2781
|
+
npx ${spec.packageName} uninstall
|
|
2782
|
+
|
|
2783
|
+
Options
|
|
2784
|
+
--api-base-url <url> ingest host (default ${hookAdapter_1.DEFAULT_API_BASE_URL})
|
|
2785
|
+
--local [port] shorthand for the local dev server (default port 4477)
|
|
2786
|
+
--tool-installation-id <id> reuse an existing pairing instead of creating one
|
|
2787
|
+
--token <eventWriteToken> reuse an existing token (stored 0600, never printed)
|
|
2788
|
+
--scope project|user where hooks are registered (default project)
|
|
2789
|
+
--project-dir <path> project root for --scope project (default cwd)
|
|
2790
|
+
--dry-run print what would change, write nothing
|
|
2791
|
+
-h, --help
|
|
2792
|
+
`;
|
|
2793
|
+
}
|
|
2794
|
+
async function runCliAgentSetup(argv, spec) {
|
|
2795
|
+
let options;
|
|
2796
|
+
try {
|
|
2797
|
+
options = parseArgs(argv, spec);
|
|
2798
|
+
} catch (error) {
|
|
2799
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
2800
|
+
return 1;
|
|
2801
|
+
}
|
|
2802
|
+
if (options.action === "help") {
|
|
2803
|
+
console.log(usage(spec));
|
|
2804
|
+
return 0;
|
|
2805
|
+
}
|
|
2806
|
+
if (options.action === "status")
|
|
2807
|
+
return printStatus(options, spec);
|
|
2808
|
+
if (options.action === "uninstall")
|
|
2809
|
+
return uninstall(options, spec);
|
|
2810
|
+
const apiBaseUrl = (options.apiBaseUrl ?? (0, credentials_1.readHostCredentials)(spec.host)?.apiBaseUrl ?? hookAdapter_1.DEFAULT_API_BASE_URL).replace(/\/$/, "");
|
|
2811
|
+
console.log(`Ascenda setup for ${spec.displayName} \u2014 ${apiBaseUrl}`);
|
|
2812
|
+
const identity = await resolveIdentity(apiBaseUrl, options, spec);
|
|
2813
|
+
if (!identity)
|
|
2814
|
+
return 1;
|
|
2815
|
+
console.log(` pairing ${identity.toolInstallationId}${identity.paired ? " (new)" : " (existing)"}`);
|
|
2816
|
+
const binary = installBinary(spec, options.dryRun);
|
|
2817
|
+
console.log(` hook binary ${binary}`);
|
|
2818
|
+
if (!options.dryRun) {
|
|
2819
|
+
(0, credentials_1.writeHostCredentials)(spec.host, { apiBaseUrl, toolInstallationId: identity.toolInstallationId, pairedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2820
|
+
}
|
|
2821
|
+
console.log(` credentials ${(0, credentials_1.credentialsFilePath)()} (tools.${spec.host})`);
|
|
2822
|
+
const settingsFile = spec.settings.settingsPath(options.scope, options.projectDir);
|
|
2823
|
+
const written = writeHookSettings(settingsFile, binary, spec, options.dryRun);
|
|
2824
|
+
if (written === null)
|
|
2825
|
+
return 1;
|
|
2826
|
+
console.log(` hooks ${settingsFile} (${spec.hookEvents.length} events${written ? "" : ", already current"})`);
|
|
2827
|
+
if (options.dryRun) {
|
|
2828
|
+
console.log("\nDry run \u2014 nothing was written.");
|
|
2829
|
+
return 0;
|
|
2830
|
+
}
|
|
2831
|
+
console.log(`
|
|
2832
|
+
Done. ${spec.restartHint}`);
|
|
2833
|
+
console.log(`Check anytime: npx ${spec.packageName} status`);
|
|
2834
|
+
return 0;
|
|
2835
|
+
}
|
|
2836
|
+
function parseArgs(argv, spec) {
|
|
2837
|
+
const options = {
|
|
2838
|
+
scope: "project",
|
|
2839
|
+
projectDir: process.cwd(),
|
|
2840
|
+
dryRun: false,
|
|
2841
|
+
action: "install"
|
|
2842
|
+
};
|
|
2843
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2844
|
+
const arg = argv[i];
|
|
2845
|
+
const next = () => {
|
|
2846
|
+
const value = argv[++i];
|
|
2847
|
+
if (value === void 0)
|
|
2848
|
+
throw new Error(`${arg} needs a value`);
|
|
2849
|
+
return value;
|
|
2850
|
+
};
|
|
2851
|
+
switch (arg) {
|
|
2852
|
+
case "setup":
|
|
2853
|
+
case "install":
|
|
2854
|
+
options.action = "install";
|
|
2855
|
+
break;
|
|
2856
|
+
case "status":
|
|
2857
|
+
options.action = "status";
|
|
2858
|
+
break;
|
|
2859
|
+
case "uninstall":
|
|
2860
|
+
options.action = "uninstall";
|
|
2861
|
+
break;
|
|
2862
|
+
case "--api-base-url":
|
|
2863
|
+
options.apiBaseUrl = next();
|
|
2864
|
+
break;
|
|
2865
|
+
case "--local": {
|
|
2866
|
+
const peek = argv[i + 1];
|
|
2867
|
+
const port = peek && /^\d+$/.test(peek) ? argv[++i] : "4477";
|
|
2868
|
+
options.apiBaseUrl = `http://localhost:${port}`;
|
|
2869
|
+
break;
|
|
2870
|
+
}
|
|
2871
|
+
case "--tool-installation-id":
|
|
2872
|
+
options.toolInstallationId = next();
|
|
2873
|
+
break;
|
|
2874
|
+
case "--token":
|
|
2875
|
+
options.token = next();
|
|
2876
|
+
break;
|
|
2877
|
+
case "--scope": {
|
|
2878
|
+
const value = next();
|
|
2879
|
+
if (value !== "project" && value !== "user")
|
|
2880
|
+
throw new Error(`--scope must be project or user, got ${value}`);
|
|
2881
|
+
options.scope = value;
|
|
2882
|
+
break;
|
|
2883
|
+
}
|
|
2884
|
+
case "--project-dir":
|
|
2885
|
+
options.projectDir = path.resolve(next());
|
|
2886
|
+
break;
|
|
2887
|
+
case "--dry-run":
|
|
2888
|
+
options.dryRun = true;
|
|
2889
|
+
break;
|
|
2890
|
+
case "-h":
|
|
2891
|
+
case "--help":
|
|
2892
|
+
options.action = "help";
|
|
2893
|
+
break;
|
|
2894
|
+
default:
|
|
2895
|
+
throw new Error(`unknown argument: ${arg}
|
|
2896
|
+
|
|
2897
|
+
${usage(spec)}`);
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
return options;
|
|
2901
|
+
}
|
|
2902
|
+
async function resolveIdentity(apiBaseUrl, options, spec) {
|
|
2903
|
+
const existingId = options.toolInstallationId ?? (0, credentials_1.readHostCredentials)(spec.host)?.toolInstallationId;
|
|
2904
|
+
if (existingId && options.token) {
|
|
2905
|
+
if (!options.dryRun)
|
|
2906
|
+
(0, tokenStore_1.persistEventWriteToken)((0, tokenStore_1.defaultTokenFilePath)(existingId), options.token);
|
|
2907
|
+
return { toolInstallationId: existingId, paired: false };
|
|
2908
|
+
}
|
|
2909
|
+
if (existingId && (0, tokenStore_1.readTokenFile)((0, tokenStore_1.defaultTokenFilePath)(existingId))) {
|
|
2910
|
+
return { toolInstallationId: existingId, paired: false };
|
|
2911
|
+
}
|
|
2912
|
+
if (options.dryRun) {
|
|
2913
|
+
return { toolInstallationId: existingId ?? `${spec.toolType}:<paired at run time>`, paired: false };
|
|
2914
|
+
}
|
|
2915
|
+
const toolInstallationId = existingId ?? `${spec.toolType}:${crypto.randomUUID()}`;
|
|
2916
|
+
let session;
|
|
2917
|
+
try {
|
|
2918
|
+
session = await (0, http_1.createPairingSession)(apiBaseUrl, toolInstallationId, spec.toolType, `${spec.displayName} on ${os.hostname()}`);
|
|
2919
|
+
} catch (error) {
|
|
2920
|
+
console.error(`
|
|
2921
|
+
Could not reach ${apiBaseUrl} to pair: ${error instanceof Error ? error.message : String(error)}`);
|
|
2922
|
+
console.error("Start the local dev server and use --local, or pass --api-base-url for your backend.");
|
|
2923
|
+
return void 0;
|
|
2924
|
+
}
|
|
2925
|
+
const token = await pollForToken(apiBaseUrl, session.pairingSessionId, session.code, session.expiresAt);
|
|
2926
|
+
if (!token)
|
|
2927
|
+
return void 0;
|
|
2928
|
+
(0, tokenStore_1.persistEventWriteToken)((0, tokenStore_1.defaultTokenFilePath)(toolInstallationId), token);
|
|
2929
|
+
return { toolInstallationId, paired: true };
|
|
2930
|
+
}
|
|
2931
|
+
async function pollForToken(apiBaseUrl, pairingSessionId, code, expiresAt) {
|
|
2932
|
+
const deadline = Math.min(Date.parse(expiresAt) || Date.now() + 3e5, Date.now() + 3e5);
|
|
2933
|
+
let announced = false;
|
|
2934
|
+
while (Date.now() < deadline) {
|
|
2935
|
+
const status = await (0, http_1.getPairingStatus)(apiBaseUrl, pairingSessionId);
|
|
2936
|
+
if (status.status === "paired" && status.eventWriteToken)
|
|
2937
|
+
return status.eventWriteToken;
|
|
2938
|
+
if (status.status === "expired" || status.status === "cancelled") {
|
|
2939
|
+
console.error(`
|
|
2940
|
+
Pairing ${status.status}. Run setup again.`);
|
|
2941
|
+
return void 0;
|
|
2942
|
+
}
|
|
2943
|
+
if (!announced) {
|
|
2944
|
+
console.log(`
|
|
2945
|
+
Confirm in the Ascenda app \u2014 code ${code}`);
|
|
2946
|
+
console.log(" Waiting...");
|
|
2947
|
+
announced = true;
|
|
2948
|
+
}
|
|
2949
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
2950
|
+
}
|
|
2951
|
+
console.error("\nPairing timed out. Run setup again.");
|
|
2952
|
+
return void 0;
|
|
2953
|
+
}
|
|
2954
|
+
function installBinary(spec, dryRun) {
|
|
2955
|
+
const target = cliAgentHookBinPath(spec.binaryName);
|
|
2956
|
+
if (dryRun)
|
|
2957
|
+
return target;
|
|
2958
|
+
const source = process.argv[1];
|
|
2959
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
2960
|
+
if (path.resolve(source) !== path.resolve(target)) {
|
|
2961
|
+
fs.copyFileSync(source, target);
|
|
2962
|
+
}
|
|
2963
|
+
if (process.platform !== "win32")
|
|
2964
|
+
fs.chmodSync(target, 493);
|
|
2965
|
+
return target;
|
|
2966
|
+
}
|
|
2967
|
+
function writeHookSettings(settingsFile, binary, spec, dryRun) {
|
|
2968
|
+
let settings = { ...spec.settings.scaffold ?? {} };
|
|
2969
|
+
const exists = fs.existsSync(settingsFile);
|
|
2970
|
+
if (exists) {
|
|
2971
|
+
const raw = fs.readFileSync(settingsFile, "utf8").trim();
|
|
2972
|
+
if (raw) {
|
|
2973
|
+
try {
|
|
2974
|
+
settings = JSON.parse(raw);
|
|
2975
|
+
} catch {
|
|
2976
|
+
console.error(`
|
|
2977
|
+
${settingsFile} is not valid JSON. Fix or move it, then run setup again.`);
|
|
2978
|
+
return null;
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
const command = hookCommand(binary);
|
|
2983
|
+
const hooks = { ...settings.hooks ?? {} };
|
|
2984
|
+
for (const event of spec.hookEvents) {
|
|
2985
|
+
const kept = (hooks[event] ?? []).filter((entry) => !isOurs(entry, spec));
|
|
2986
|
+
hooks[event] = [...kept, spec.settings.entry(command, event)];
|
|
2987
|
+
}
|
|
2988
|
+
const updated = { ...settings, hooks };
|
|
2989
|
+
const serialised = `${JSON.stringify(updated, null, 2)}
|
|
2990
|
+
`;
|
|
2991
|
+
if (exists && fs.readFileSync(settingsFile, "utf8") === serialised)
|
|
2992
|
+
return false;
|
|
2993
|
+
if (dryRun) {
|
|
2994
|
+
console.log(`
|
|
2995
|
+
--- ${settingsFile} (dry run) ---
|
|
2996
|
+
${serialised}`);
|
|
2997
|
+
return true;
|
|
2998
|
+
}
|
|
2999
|
+
if (exists)
|
|
3000
|
+
fs.copyFileSync(settingsFile, `${settingsFile}.ascenda-backup`);
|
|
3001
|
+
fs.mkdirSync(path.dirname(settingsFile), { recursive: true });
|
|
3002
|
+
fs.writeFileSync(settingsFile, serialised, "utf8");
|
|
3003
|
+
return true;
|
|
3004
|
+
}
|
|
3005
|
+
function hookCommand(binary) {
|
|
3006
|
+
return `"${process.execPath}" "${binary}"`;
|
|
3007
|
+
}
|
|
3008
|
+
function isOurs(entry, spec) {
|
|
3009
|
+
const command = spec.settings.commandOf(entry);
|
|
3010
|
+
return typeof command === "string" && command.includes(spec.binaryName);
|
|
3011
|
+
}
|
|
3012
|
+
function findStaleHookCommands(settings, binary, spec) {
|
|
3013
|
+
const stale = /* @__PURE__ */ new Set();
|
|
3014
|
+
for (const entries of Object.values(settings.hooks ?? {})) {
|
|
3015
|
+
for (const entry of entries ?? []) {
|
|
3016
|
+
const command = spec.settings.commandOf(entry);
|
|
3017
|
+
if (typeof command !== "string")
|
|
3018
|
+
continue;
|
|
3019
|
+
if (!/ascenda/i.test(command) || command.includes(binary))
|
|
3020
|
+
continue;
|
|
3021
|
+
stale.add(command);
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
return [...stale];
|
|
3025
|
+
}
|
|
3026
|
+
function readSettings(settingsFile) {
|
|
3027
|
+
try {
|
|
3028
|
+
return JSON.parse(fs.readFileSync(settingsFile, "utf8"));
|
|
3029
|
+
} catch {
|
|
3030
|
+
return {};
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
function printStatus(options, spec) {
|
|
3034
|
+
const credentials = (0, credentials_1.readHostCredentials)(spec.host);
|
|
3035
|
+
const settingsFile = spec.settings.settingsPath(options.scope, options.projectDir);
|
|
3036
|
+
const binary = cliAgentHookBinPath(spec.binaryName);
|
|
3037
|
+
const tokenFile = credentials?.toolInstallationId ? (0, tokenStore_1.defaultTokenFilePath)(credentials.toolInstallationId) : void 0;
|
|
3038
|
+
const settings = readSettings(settingsFile);
|
|
3039
|
+
const registered = spec.hookEvents.filter((event) => (settings.hooks?.[event] ?? []).some((entry) => isOurs(entry, spec))).length;
|
|
3040
|
+
const stale = findStaleHookCommands(settings, binary, spec);
|
|
3041
|
+
console.log(`api base url ${credentials?.apiBaseUrl ?? "\u2014 not configured"}`);
|
|
3042
|
+
console.log(`pairing ${credentials?.toolInstallationId ?? "\u2014 not paired"}`);
|
|
3043
|
+
console.log(`token ${tokenFile && (0, tokenStore_1.readTokenFile)(tokenFile) ? "present" : "\u2014 missing"}`);
|
|
3044
|
+
console.log(`hook binary ${fs.existsSync(binary) ? binary : "\u2014 not installed"}`);
|
|
3045
|
+
console.log(`hooks ${registered}/${spec.hookEvents.length} registered in ${settingsFile}`);
|
|
3046
|
+
if (stale.length) {
|
|
3047
|
+
console.log(`stale hooks ${stale.length} not pointing at the installed binary \u2014 each one fails silently per event:`);
|
|
3048
|
+
for (const command of stale)
|
|
3049
|
+
console.log(` ${command}`);
|
|
3050
|
+
console.log(` Remove them from ${settingsFile} by hand; setup cannot tell them from a hook you wrote.`);
|
|
3051
|
+
}
|
|
3052
|
+
const healthy = credentials?.toolInstallationId && registered === spec.hookEvents.length && fs.existsSync(binary) && !stale.length;
|
|
3053
|
+
return healthy ? 0 : 1;
|
|
3054
|
+
}
|
|
3055
|
+
function uninstall(options, spec) {
|
|
3056
|
+
const settingsFile = spec.settings.settingsPath(options.scope, options.projectDir);
|
|
3057
|
+
if (fs.existsSync(settingsFile)) {
|
|
3058
|
+
try {
|
|
3059
|
+
const settings = JSON.parse(fs.readFileSync(settingsFile, "utf8"));
|
|
3060
|
+
const hooks = { ...settings.hooks ?? {} };
|
|
3061
|
+
for (const event of Object.keys(hooks)) {
|
|
3062
|
+
const kept = hooks[event].filter((entry) => !isOurs(entry, spec));
|
|
3063
|
+
if (kept.length)
|
|
3064
|
+
hooks[event] = kept;
|
|
3065
|
+
else
|
|
3066
|
+
delete hooks[event];
|
|
3067
|
+
}
|
|
3068
|
+
const updated = { ...settings, hooks };
|
|
3069
|
+
if (!Object.keys(hooks).length)
|
|
3070
|
+
delete updated.hooks;
|
|
3071
|
+
fs.copyFileSync(settingsFile, `${settingsFile}.ascenda-backup`);
|
|
3072
|
+
fs.writeFileSync(settingsFile, `${JSON.stringify(updated, null, 2)}
|
|
3073
|
+
`, "utf8");
|
|
3074
|
+
console.log(`hooks removed from ${settingsFile}`);
|
|
3075
|
+
} catch {
|
|
3076
|
+
console.error(`could not parse ${settingsFile} \u2014 remove the ascenda hook entries by hand`);
|
|
3077
|
+
return 1;
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
const binary = cliAgentHookBinPath(spec.binaryName);
|
|
3081
|
+
if (fs.existsSync(binary)) {
|
|
3082
|
+
fs.rmSync(binary);
|
|
3083
|
+
console.log(`removed ${binary}`);
|
|
3084
|
+
}
|
|
3085
|
+
if ((0, credentials_1.readHostCredentials)(spec.host)) {
|
|
3086
|
+
(0, credentials_1.removeHostCredentials)(spec.host);
|
|
3087
|
+
console.log(`removed tools.${spec.host} from ${(0, credentials_1.credentialsFilePath)()}`);
|
|
3088
|
+
}
|
|
3089
|
+
console.log(`tokens left in ${path.join((0, tokenStore_1.ascendaHome)(), "tokens")} \u2014 revoke in the Ascenda app to invalidate them`);
|
|
3090
|
+
return 0;
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
});
|
|
3094
|
+
|
|
1513
3095
|
// ../packages/tool-kit/out/turnState.js
|
|
1514
3096
|
var require_turnState = __commonJS({
|
|
1515
3097
|
"../packages/tool-kit/out/turnState.js"(exports) {
|
|
@@ -1723,8 +3305,9 @@ var require_out2 = __commonJS({
|
|
|
1723
3305
|
"../packages/tool-kit/out/index.js"(exports) {
|
|
1724
3306
|
"use strict";
|
|
1725
3307
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1726
|
-
exports.
|
|
1727
|
-
exports.
|
|
3308
|
+
exports.consumeTurnDurationMs = exports.writeTopLevelCredentials = exports.writeMachineCredentials = exports.writeHostCredentials = exports.removeHostCredentials = exports.readMachineCredentials = exports.readHostCredentials = exports.credentialsFilePath = exports.writeHookSettings = exports.runCliAgentSetup = exports.isCliAgentManagementCommand = exports.findStaleHookCommands = exports.cliAgentHookBinPath = exports.resolveContextHashes = exports.resolveCliAgentInstallationId = exports.loadCliAgentConfig = exports.deliverHookEvents = exports.MissingInstallationIdError = exports.DEFAULT_API_BASE_URL = exports.resolveEventLogPath = exports.expandUserPath = exports.appendEventLog = exports.EVENT_LOG_ENV_VAR = exports.buildEventPayload = exports.AscendaSemanticEventError = exports.AscendaEventSender = exports.mintIdempotencyKey = 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.classifyModelClass = exports.autonomyBand = exports.invitesDebrief = exports.classifyWorkMilestone = exports.isReworkGitAction = exports.classifyGitAction = exports.isVerificationCommand = exports.classifyCommand = void 0;
|
|
3309
|
+
exports.postToolEvent = exports.renewToolToken = exports.getPairingStatus = exports.createPairingSession = exports.AscendaApiError = exports.liveBusSocketCandidates = exports.liveBusSocketPath = exports.bucketPromptSize = exports.emitLiveSignal = exports.recordForgeProjectAlias = exports.forgeFullNameFromConfig = exports.readForgeFullName = exports.parseForgeFullName = exports.forgeProjectHash = exports.workContextRegistryFilePath = exports.readWorkContextRegistry = exports.recordWorkContextAlias = exports.recordWorkContext = exports.readBranchName = exports.normalizeBranchName = exports.deriveBranchHashForCwd = exports.deriveBranchHash = exports.deriveWorkContext = exports.hashWithMachineSalt = exports.readOrCreateMachineSalt = exports.machineSaltFilePath = exports.enforceOutboxBounds = exports.claimOutbox = exports.readOutboxSummary = exports.appendToOutbox = exports.defaultOutboxFilePath = exports.outboxDrainEnabled = exports.DEFAULT_OUTBOX_DRAIN_BATCH_SIZE = exports.DEFAULT_OUTBOX_MAX_AGE_MS = exports.DEFAULT_OUTBOX_MAX_ENTRIES = exports.OUTBOX_DRAIN_ENV_VAR = exports.recordOutboxDiscard = exports.unresolvedToolInstallationId = exports.unresolvedStateFilePath = exports.markFailureNotified = exports.shouldAnnounceFailure = exports.recordSendOutcome = exports.readCollectorState = exports.defaultStateFilePath = exports.readTokenFile = exports.persistEventWriteToken = exports.listPersistedToolInstallationIds = exports.defaultTokenFilePath = exports.ascendaHome = exports.recordTurnStart = void 0;
|
|
3310
|
+
exports.isRetryableStatus = exports.parseIngestResponse = exports.postToolEventsBatch = void 0;
|
|
1728
3311
|
var commandClassifier_1 = require_commandClassifier();
|
|
1729
3312
|
Object.defineProperty(exports, "classifyCommand", { enumerable: true, get: function() {
|
|
1730
3313
|
return commandClassifier_1.classifyCommand;
|
|
@@ -1746,6 +3329,14 @@ var require_out2 = __commonJS({
|
|
|
1746
3329
|
Object.defineProperty(exports, "invitesDebrief", { enumerable: true, get: function() {
|
|
1747
3330
|
return workMilestoneClassifier_1.invitesDebrief;
|
|
1748
3331
|
} });
|
|
3332
|
+
var autonomyBand_1 = require_autonomyBand();
|
|
3333
|
+
Object.defineProperty(exports, "autonomyBand", { enumerable: true, get: function() {
|
|
3334
|
+
return autonomyBand_1.autonomyBand;
|
|
3335
|
+
} });
|
|
3336
|
+
var modelClassifier_1 = require_modelClassifier();
|
|
3337
|
+
Object.defineProperty(exports, "classifyModelClass", { enumerable: true, get: function() {
|
|
3338
|
+
return modelClassifier_1.classifyModelClass;
|
|
3339
|
+
} });
|
|
1749
3340
|
var buckets_1 = require_buckets();
|
|
1750
3341
|
Object.defineProperty(exports, "bucketLinesChanged", { enumerable: true, get: function() {
|
|
1751
3342
|
return buckets_1.bucketLinesChanged;
|
|
@@ -1794,6 +3385,9 @@ var require_out2 = __commonJS({
|
|
|
1794
3385
|
Object.defineProperty(exports, "looksLikeCorrection", { enumerable: true, get: function() {
|
|
1795
3386
|
return payload_1.looksLikeCorrection;
|
|
1796
3387
|
} });
|
|
3388
|
+
Object.defineProperty(exports, "mintIdempotencyKey", { enumerable: true, get: function() {
|
|
3389
|
+
return payload_1.mintIdempotencyKey;
|
|
3390
|
+
} });
|
|
1797
3391
|
var eventSender_1 = require_eventSender();
|
|
1798
3392
|
Object.defineProperty(exports, "AscendaEventSender", { enumerable: true, get: function() {
|
|
1799
3393
|
return eventSender_1.AscendaEventSender;
|
|
@@ -1821,15 +3415,59 @@ var require_out2 = __commonJS({
|
|
|
1821
3415
|
Object.defineProperty(exports, "DEFAULT_API_BASE_URL", { enumerable: true, get: function() {
|
|
1822
3416
|
return hookAdapter_1.DEFAULT_API_BASE_URL;
|
|
1823
3417
|
} });
|
|
3418
|
+
Object.defineProperty(exports, "MissingInstallationIdError", { enumerable: true, get: function() {
|
|
3419
|
+
return hookAdapter_1.MissingInstallationIdError;
|
|
3420
|
+
} });
|
|
1824
3421
|
Object.defineProperty(exports, "deliverHookEvents", { enumerable: true, get: function() {
|
|
1825
3422
|
return hookAdapter_1.deliverHookEvents;
|
|
1826
3423
|
} });
|
|
1827
3424
|
Object.defineProperty(exports, "loadCliAgentConfig", { enumerable: true, get: function() {
|
|
1828
3425
|
return hookAdapter_1.loadCliAgentConfig;
|
|
1829
3426
|
} });
|
|
3427
|
+
Object.defineProperty(exports, "resolveCliAgentInstallationId", { enumerable: true, get: function() {
|
|
3428
|
+
return hookAdapter_1.resolveCliAgentInstallationId;
|
|
3429
|
+
} });
|
|
1830
3430
|
Object.defineProperty(exports, "resolveContextHashes", { enumerable: true, get: function() {
|
|
1831
3431
|
return hookAdapter_1.resolveContextHashes;
|
|
1832
3432
|
} });
|
|
3433
|
+
var cliAgentSetup_1 = require_cliAgentSetup();
|
|
3434
|
+
Object.defineProperty(exports, "cliAgentHookBinPath", { enumerable: true, get: function() {
|
|
3435
|
+
return cliAgentSetup_1.cliAgentHookBinPath;
|
|
3436
|
+
} });
|
|
3437
|
+
Object.defineProperty(exports, "findStaleHookCommands", { enumerable: true, get: function() {
|
|
3438
|
+
return cliAgentSetup_1.findStaleHookCommands;
|
|
3439
|
+
} });
|
|
3440
|
+
Object.defineProperty(exports, "isCliAgentManagementCommand", { enumerable: true, get: function() {
|
|
3441
|
+
return cliAgentSetup_1.isCliAgentManagementCommand;
|
|
3442
|
+
} });
|
|
3443
|
+
Object.defineProperty(exports, "runCliAgentSetup", { enumerable: true, get: function() {
|
|
3444
|
+
return cliAgentSetup_1.runCliAgentSetup;
|
|
3445
|
+
} });
|
|
3446
|
+
Object.defineProperty(exports, "writeHookSettings", { enumerable: true, get: function() {
|
|
3447
|
+
return cliAgentSetup_1.writeHookSettings;
|
|
3448
|
+
} });
|
|
3449
|
+
var credentials_1 = require_credentials();
|
|
3450
|
+
Object.defineProperty(exports, "credentialsFilePath", { enumerable: true, get: function() {
|
|
3451
|
+
return credentials_1.credentialsFilePath;
|
|
3452
|
+
} });
|
|
3453
|
+
Object.defineProperty(exports, "readHostCredentials", { enumerable: true, get: function() {
|
|
3454
|
+
return credentials_1.readHostCredentials;
|
|
3455
|
+
} });
|
|
3456
|
+
Object.defineProperty(exports, "readMachineCredentials", { enumerable: true, get: function() {
|
|
3457
|
+
return credentials_1.readMachineCredentials;
|
|
3458
|
+
} });
|
|
3459
|
+
Object.defineProperty(exports, "removeHostCredentials", { enumerable: true, get: function() {
|
|
3460
|
+
return credentials_1.removeHostCredentials;
|
|
3461
|
+
} });
|
|
3462
|
+
Object.defineProperty(exports, "writeHostCredentials", { enumerable: true, get: function() {
|
|
3463
|
+
return credentials_1.writeHostCredentials;
|
|
3464
|
+
} });
|
|
3465
|
+
Object.defineProperty(exports, "writeMachineCredentials", { enumerable: true, get: function() {
|
|
3466
|
+
return credentials_1.writeMachineCredentials;
|
|
3467
|
+
} });
|
|
3468
|
+
Object.defineProperty(exports, "writeTopLevelCredentials", { enumerable: true, get: function() {
|
|
3469
|
+
return credentials_1.writeTopLevelCredentials;
|
|
3470
|
+
} });
|
|
1833
3471
|
var turnState_1 = require_turnState();
|
|
1834
3472
|
Object.defineProperty(exports, "consumeTurnDurationMs", { enumerable: true, get: function() {
|
|
1835
3473
|
return turnState_1.consumeTurnDurationMs;
|
|
@@ -1838,9 +3476,15 @@ var require_out2 = __commonJS({
|
|
|
1838
3476
|
return turnState_1.recordTurnStart;
|
|
1839
3477
|
} });
|
|
1840
3478
|
var tokenStore_1 = require_tokenStore();
|
|
3479
|
+
Object.defineProperty(exports, "ascendaHome", { enumerable: true, get: function() {
|
|
3480
|
+
return tokenStore_1.ascendaHome;
|
|
3481
|
+
} });
|
|
1841
3482
|
Object.defineProperty(exports, "defaultTokenFilePath", { enumerable: true, get: function() {
|
|
1842
3483
|
return tokenStore_1.defaultTokenFilePath;
|
|
1843
3484
|
} });
|
|
3485
|
+
Object.defineProperty(exports, "listPersistedToolInstallationIds", { enumerable: true, get: function() {
|
|
3486
|
+
return tokenStore_1.listPersistedToolInstallationIds;
|
|
3487
|
+
} });
|
|
1844
3488
|
Object.defineProperty(exports, "persistEventWriteToken", { enumerable: true, get: function() {
|
|
1845
3489
|
return tokenStore_1.persistEventWriteToken;
|
|
1846
3490
|
} });
|
|
@@ -1863,6 +3507,46 @@ var require_out2 = __commonJS({
|
|
|
1863
3507
|
Object.defineProperty(exports, "markFailureNotified", { enumerable: true, get: function() {
|
|
1864
3508
|
return stateStore_1.markFailureNotified;
|
|
1865
3509
|
} });
|
|
3510
|
+
Object.defineProperty(exports, "unresolvedStateFilePath", { enumerable: true, get: function() {
|
|
3511
|
+
return stateStore_1.unresolvedStateFilePath;
|
|
3512
|
+
} });
|
|
3513
|
+
Object.defineProperty(exports, "unresolvedToolInstallationId", { enumerable: true, get: function() {
|
|
3514
|
+
return stateStore_1.unresolvedToolInstallationId;
|
|
3515
|
+
} });
|
|
3516
|
+
Object.defineProperty(exports, "recordOutboxDiscard", { enumerable: true, get: function() {
|
|
3517
|
+
return stateStore_1.recordOutboxDiscard;
|
|
3518
|
+
} });
|
|
3519
|
+
var outbox_1 = require_outbox();
|
|
3520
|
+
Object.defineProperty(exports, "OUTBOX_DRAIN_ENV_VAR", { enumerable: true, get: function() {
|
|
3521
|
+
return outbox_1.OUTBOX_DRAIN_ENV_VAR;
|
|
3522
|
+
} });
|
|
3523
|
+
Object.defineProperty(exports, "DEFAULT_OUTBOX_MAX_ENTRIES", { enumerable: true, get: function() {
|
|
3524
|
+
return outbox_1.DEFAULT_OUTBOX_MAX_ENTRIES;
|
|
3525
|
+
} });
|
|
3526
|
+
Object.defineProperty(exports, "DEFAULT_OUTBOX_MAX_AGE_MS", { enumerable: true, get: function() {
|
|
3527
|
+
return outbox_1.DEFAULT_OUTBOX_MAX_AGE_MS;
|
|
3528
|
+
} });
|
|
3529
|
+
Object.defineProperty(exports, "DEFAULT_OUTBOX_DRAIN_BATCH_SIZE", { enumerable: true, get: function() {
|
|
3530
|
+
return outbox_1.DEFAULT_OUTBOX_DRAIN_BATCH_SIZE;
|
|
3531
|
+
} });
|
|
3532
|
+
Object.defineProperty(exports, "outboxDrainEnabled", { enumerable: true, get: function() {
|
|
3533
|
+
return outbox_1.outboxDrainEnabled;
|
|
3534
|
+
} });
|
|
3535
|
+
Object.defineProperty(exports, "defaultOutboxFilePath", { enumerable: true, get: function() {
|
|
3536
|
+
return outbox_1.defaultOutboxFilePath;
|
|
3537
|
+
} });
|
|
3538
|
+
Object.defineProperty(exports, "appendToOutbox", { enumerable: true, get: function() {
|
|
3539
|
+
return outbox_1.appendToOutbox;
|
|
3540
|
+
} });
|
|
3541
|
+
Object.defineProperty(exports, "readOutboxSummary", { enumerable: true, get: function() {
|
|
3542
|
+
return outbox_1.readOutboxSummary;
|
|
3543
|
+
} });
|
|
3544
|
+
Object.defineProperty(exports, "claimOutbox", { enumerable: true, get: function() {
|
|
3545
|
+
return outbox_1.claimOutbox;
|
|
3546
|
+
} });
|
|
3547
|
+
Object.defineProperty(exports, "enforceOutboxBounds", { enumerable: true, get: function() {
|
|
3548
|
+
return outbox_1.enforceOutboxBounds;
|
|
3549
|
+
} });
|
|
1866
3550
|
var salt_1 = require_salt();
|
|
1867
3551
|
Object.defineProperty(exports, "machineSaltFilePath", { enumerable: true, get: function() {
|
|
1868
3552
|
return salt_1.machineSaltFilePath;
|
|
@@ -1877,6 +3561,18 @@ var require_out2 = __commonJS({
|
|
|
1877
3561
|
Object.defineProperty(exports, "deriveWorkContext", { enumerable: true, get: function() {
|
|
1878
3562
|
return workContext_1.deriveWorkContext;
|
|
1879
3563
|
} });
|
|
3564
|
+
Object.defineProperty(exports, "deriveBranchHash", { enumerable: true, get: function() {
|
|
3565
|
+
return workContext_1.deriveBranchHash;
|
|
3566
|
+
} });
|
|
3567
|
+
Object.defineProperty(exports, "deriveBranchHashForCwd", { enumerable: true, get: function() {
|
|
3568
|
+
return workContext_1.deriveBranchHashForCwd;
|
|
3569
|
+
} });
|
|
3570
|
+
Object.defineProperty(exports, "normalizeBranchName", { enumerable: true, get: function() {
|
|
3571
|
+
return workContext_1.normalizeBranchName;
|
|
3572
|
+
} });
|
|
3573
|
+
Object.defineProperty(exports, "readBranchName", { enumerable: true, get: function() {
|
|
3574
|
+
return workContext_1.readBranchName;
|
|
3575
|
+
} });
|
|
1880
3576
|
var contextRegistry_1 = require_contextRegistry();
|
|
1881
3577
|
Object.defineProperty(exports, "recordWorkContext", { enumerable: true, get: function() {
|
|
1882
3578
|
return contextRegistry_1.recordWorkContext;
|
|
@@ -1890,6 +3586,22 @@ var require_out2 = __commonJS({
|
|
|
1890
3586
|
Object.defineProperty(exports, "workContextRegistryFilePath", { enumerable: true, get: function() {
|
|
1891
3587
|
return contextRegistry_1.workContextRegistryFilePath;
|
|
1892
3588
|
} });
|
|
3589
|
+
var forgeProject_1 = require_forgeProject();
|
|
3590
|
+
Object.defineProperty(exports, "forgeProjectHash", { enumerable: true, get: function() {
|
|
3591
|
+
return forgeProject_1.forgeProjectHash;
|
|
3592
|
+
} });
|
|
3593
|
+
Object.defineProperty(exports, "parseForgeFullName", { enumerable: true, get: function() {
|
|
3594
|
+
return forgeProject_1.parseForgeFullName;
|
|
3595
|
+
} });
|
|
3596
|
+
Object.defineProperty(exports, "readForgeFullName", { enumerable: true, get: function() {
|
|
3597
|
+
return forgeProject_1.readForgeFullName;
|
|
3598
|
+
} });
|
|
3599
|
+
Object.defineProperty(exports, "forgeFullNameFromConfig", { enumerable: true, get: function() {
|
|
3600
|
+
return forgeProject_1.forgeFullNameFromConfig;
|
|
3601
|
+
} });
|
|
3602
|
+
Object.defineProperty(exports, "recordForgeProjectAlias", { enumerable: true, get: function() {
|
|
3603
|
+
return forgeProject_1.recordForgeProjectAlias;
|
|
3604
|
+
} });
|
|
1893
3605
|
var liveBus_1 = require_liveBus();
|
|
1894
3606
|
Object.defineProperty(exports, "emitLiveSignal", { enumerable: true, get: function() {
|
|
1895
3607
|
return liveBus_1.emitLiveSignal;
|
|
@@ -1932,7 +3644,7 @@ var require_out2 = __commonJS({
|
|
|
1932
3644
|
});
|
|
1933
3645
|
|
|
1934
3646
|
// src/cli.ts
|
|
1935
|
-
var
|
|
3647
|
+
var import_tool_kit3 = __toESM(require_out2(), 1);
|
|
1936
3648
|
import { readFile } from "node:fs/promises";
|
|
1937
3649
|
|
|
1938
3650
|
// src/config.ts
|
|
@@ -1962,6 +3674,7 @@ function normalizeToolInstallationId(value) {
|
|
|
1962
3674
|
}
|
|
1963
3675
|
|
|
1964
3676
|
// src/mapForgeEvent.ts
|
|
3677
|
+
var import_tool_kit2 = __toESM(require_out2(), 1);
|
|
1965
3678
|
function mapForgeEvent(eventName, payload, viewerLogin) {
|
|
1966
3679
|
if (!eventName || !viewerLogin) return [];
|
|
1967
3680
|
const action = str(payload["action"]);
|
|
@@ -2013,20 +3726,20 @@ function base(payload) {
|
|
|
2013
3726
|
host: "github",
|
|
2014
3727
|
// Hashed, never the name. "Is it always the same repository" stays
|
|
2015
3728
|
// answerable; which repository does not travel.
|
|
2016
|
-
|
|
3729
|
+
//
|
|
3730
|
+
// The digest is an UNSALTED FNV-1a of `owner/repo`, and this step stays
|
|
3731
|
+
// deliberately salt-free: it runs in CI from a webhook payload, where the
|
|
3732
|
+
// only place a machine salt could come from is a repository secret — which
|
|
3733
|
+
// is to say, from everyone who can read the repository's settings. The
|
|
3734
|
+
// function now lives in tool-kit so a developer's own machine, which holds
|
|
3735
|
+
// both identities, can compute this exact digest and file it beside its
|
|
3736
|
+
// own; nothing about what this step emits has changed.
|
|
3737
|
+
...repo ? { projectHash: (0, import_tool_kit2.forgeProjectHash)(repo) } : {}
|
|
2017
3738
|
};
|
|
2018
3739
|
}
|
|
2019
3740
|
function reviewState(state) {
|
|
2020
3741
|
return state?.toLowerCase() === "approved" ? "success" : "unknown";
|
|
2021
3742
|
}
|
|
2022
|
-
function hash(value) {
|
|
2023
|
-
let h = 2166136261;
|
|
2024
|
-
for (let i = 0; i < value.length; i++) {
|
|
2025
|
-
h ^= value.charCodeAt(i);
|
|
2026
|
-
h = Math.imul(h, 16777619) >>> 0;
|
|
2027
|
-
}
|
|
2028
|
-
return h.toString(16).padStart(8, "0");
|
|
2029
|
-
}
|
|
2030
3743
|
function obj(value) {
|
|
2031
3744
|
return value && typeof value === "object" ? value : {};
|
|
2032
3745
|
}
|
|
@@ -2042,7 +3755,7 @@ async function main() {
|
|
|
2042
3755
|
if (!payload) return;
|
|
2043
3756
|
const events = mapForgeEvent(eventName, payload, config.viewerLogin);
|
|
2044
3757
|
if (events.length === 0) return;
|
|
2045
|
-
const sender = new
|
|
3758
|
+
const sender = new import_tool_kit3.AscendaEventSender({
|
|
2046
3759
|
apiBaseUrl: config.apiBaseUrl,
|
|
2047
3760
|
toolInstallationId: config.toolInstallationId,
|
|
2048
3761
|
source: "code_forge",
|