@ascenda-one/cursor-hooks 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/LICENSE +202 -0
- package/README.md +73 -0
- package/dist/cli.js +3847 -0
- package/dist/cli.js.map +1 -0
- package/dist/mapCursorEvent.js +151 -0
- package/dist/mapCursorEvent.js.map +1 -0
- package/dist/setup.js +28 -0
- package/dist/setup.js.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/examples/hooks.json +45 -0
- package/package.json +30 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,3847 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
10
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
11
|
+
}) : x)(function(x) {
|
|
12
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
13
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
14
|
+
});
|
|
15
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
16
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
27
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
28
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
29
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
30
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
31
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
32
|
+
mod
|
|
33
|
+
));
|
|
34
|
+
|
|
35
|
+
// ../packages/tool-kit/out/commandClassifier.js
|
|
36
|
+
var require_commandClassifier = __commonJS({
|
|
37
|
+
"../packages/tool-kit/out/commandClassifier.js"(exports) {
|
|
38
|
+
"use strict";
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.classifyCommand = classifyCommand2;
|
|
41
|
+
exports.isVerificationCommand = isVerificationCommand2;
|
|
42
|
+
function classifyCommand2(command) {
|
|
43
|
+
if (!command)
|
|
44
|
+
return "unknown";
|
|
45
|
+
const value = command.toLowerCase().trim();
|
|
46
|
+
if (/\b(test|jest|vitest|mocha|pytest|rspec|go test|cargo test|dotnet test|xcodebuild test)\b/.test(value) || /\bnpm\s+(run\s+)?test\b/.test(value) || /\byarn\s+test\b/.test(value) || /\bpnpm\s+(run\s+)?test\b/.test(value))
|
|
47
|
+
return "test";
|
|
48
|
+
if (/\b(lint|eslint|ruff|flake8|pylint|rubocop)\b/.test(value) || /\bnpm\s+(run\s+)?lint\b/.test(value))
|
|
49
|
+
return "lint";
|
|
50
|
+
if (/\b(tsc|typecheck|mypy|pyright|sorbet|flow)\b/.test(value) || /\bnpm\s+(run\s+)?typecheck\b/.test(value))
|
|
51
|
+
return "typecheck";
|
|
52
|
+
if (/\b(build|webpack|vite build|next build|turbo build|cargo build|go build|dotnet build|xcodebuild)\b/.test(value) || /\bnpm\s+(run\s+)?build\b/.test(value))
|
|
53
|
+
return "build";
|
|
54
|
+
if (/\b(git)\b/.test(value))
|
|
55
|
+
return "git";
|
|
56
|
+
if (/\b(npm install|yarn install|pnpm install|bun install|pip install|poetry install|bundle install)\b/.test(value))
|
|
57
|
+
return "install";
|
|
58
|
+
if (/\b(npm start|npm run dev|yarn dev|pnpm dev|next dev|vite|node|python|tsx|ts-node)\b/.test(value))
|
|
59
|
+
return "run";
|
|
60
|
+
return "unknown";
|
|
61
|
+
}
|
|
62
|
+
function isVerificationCommand2(commandClass) {
|
|
63
|
+
return commandClass === "test" || commandClass === "lint" || commandClass === "typecheck" || commandClass === "build";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ../packages/tool-kit/out/commandHeads.js
|
|
69
|
+
var require_commandHeads = __commonJS({
|
|
70
|
+
"../packages/tool-kit/out/commandHeads.js"(exports) {
|
|
71
|
+
"use strict";
|
|
72
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
73
|
+
exports.commandHeads = commandHeads;
|
|
74
|
+
function commandHeads(value) {
|
|
75
|
+
const stripped = stripQuoted(stripHeredocBodies(value));
|
|
76
|
+
return stripped.split(/[;&|()\n]+/).map((segment) => {
|
|
77
|
+
let head = segment.trim();
|
|
78
|
+
for (; ; ) {
|
|
79
|
+
const env = /^[a-z_][a-z0-9_]*=\S*\s+/.exec(head);
|
|
80
|
+
if (!env)
|
|
81
|
+
break;
|
|
82
|
+
head = head.slice(env[0].length);
|
|
83
|
+
}
|
|
84
|
+
return head;
|
|
85
|
+
}).filter((head) => head.length > 0);
|
|
86
|
+
}
|
|
87
|
+
function stripHeredocBodies(value) {
|
|
88
|
+
const marker = /<<-?\s*(["']?)(\w+)\1/;
|
|
89
|
+
let kept = "";
|
|
90
|
+
let rest = value;
|
|
91
|
+
for (; ; ) {
|
|
92
|
+
const opened = marker.exec(rest);
|
|
93
|
+
if (!opened)
|
|
94
|
+
return kept + rest;
|
|
95
|
+
const introLineEnd = rest.indexOf("\n", opened.index + opened[0].length);
|
|
96
|
+
if (introLineEnd === -1)
|
|
97
|
+
return kept + rest;
|
|
98
|
+
kept += rest.slice(0, introLineEnd + 1);
|
|
99
|
+
const body = rest.slice(introLineEnd + 1);
|
|
100
|
+
const terminator = new RegExp(`^\\s*${opened[2]}\\s*$`, "m").exec(body);
|
|
101
|
+
if (!terminator)
|
|
102
|
+
return kept;
|
|
103
|
+
rest = body.slice(terminator.index + terminator[0].length);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function stripQuoted(value) {
|
|
107
|
+
let out = "";
|
|
108
|
+
let i = 0;
|
|
109
|
+
while (i < value.length) {
|
|
110
|
+
const ch = value[i];
|
|
111
|
+
if (ch === "'") {
|
|
112
|
+
const close = value.indexOf("'", i + 1);
|
|
113
|
+
if (close === -1)
|
|
114
|
+
return out;
|
|
115
|
+
i = close + 1;
|
|
116
|
+
} else if (ch === '"') {
|
|
117
|
+
let j = i + 1;
|
|
118
|
+
while (j < value.length && value[j] !== '"') {
|
|
119
|
+
j += value[j] === "\\" ? 2 : 1;
|
|
120
|
+
}
|
|
121
|
+
if (j >= value.length)
|
|
122
|
+
return out;
|
|
123
|
+
i = j + 1;
|
|
124
|
+
} else if (ch === "\\") {
|
|
125
|
+
out += value.slice(i, i + 2);
|
|
126
|
+
i += 2;
|
|
127
|
+
} else {
|
|
128
|
+
out += ch;
|
|
129
|
+
i += 1;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// ../packages/tool-kit/out/gitActionClassifier.js
|
|
138
|
+
var require_gitActionClassifier = __commonJS({
|
|
139
|
+
"../packages/tool-kit/out/gitActionClassifier.js"(exports) {
|
|
140
|
+
"use strict";
|
|
141
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
142
|
+
exports.classifyGitAction = classifyGitAction;
|
|
143
|
+
exports.isReworkGitAction = isReworkGitAction;
|
|
144
|
+
var commandHeads_1 = require_commandHeads();
|
|
145
|
+
function classifyGitAction(command) {
|
|
146
|
+
if (!command)
|
|
147
|
+
return void 0;
|
|
148
|
+
const value = command.toLowerCase().trim();
|
|
149
|
+
if (!/\bgit\b/.test(value))
|
|
150
|
+
return void 0;
|
|
151
|
+
const heads = (0, commandHeads_1.commandHeads)(value);
|
|
152
|
+
if (heads.some((h) => /^git\s+commit\b/.test(h) && /\s--amend\b/.test(h)))
|
|
153
|
+
return "amend";
|
|
154
|
+
if (heads.some((h) => /^git\s+revert\b/.test(h)))
|
|
155
|
+
return "revert";
|
|
156
|
+
if (heads.some((h) => /^git\s+reset\b/.test(h) && /\s--hard\b/.test(h)))
|
|
157
|
+
return "reset_hard";
|
|
158
|
+
if (heads.some((h) => /^git\s+restore\b/.test(h) && !/\s--staged\b/.test(h)))
|
|
159
|
+
return "restore";
|
|
160
|
+
if (heads.some((h) => /^git\s+checkout\b.*\s--\s/.test(h)))
|
|
161
|
+
return "restore";
|
|
162
|
+
if (heads.some((h) => /^git\s+push\b/.test(h)))
|
|
163
|
+
return "push";
|
|
164
|
+
if (heads.some((h) => /^git\s+commit\b/.test(h)))
|
|
165
|
+
return "commit";
|
|
166
|
+
return void 0;
|
|
167
|
+
}
|
|
168
|
+
function isReworkGitAction(action) {
|
|
169
|
+
return action === "revert" || action === "reset_hard" || action === "restore";
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// ../packages/tool-kit/out/workMilestoneClassifier.js
|
|
175
|
+
var require_workMilestoneClassifier = __commonJS({
|
|
176
|
+
"../packages/tool-kit/out/workMilestoneClassifier.js"(exports) {
|
|
177
|
+
"use strict";
|
|
178
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
179
|
+
exports.classifyWorkMilestone = classifyWorkMilestone;
|
|
180
|
+
exports.invitesDebrief = invitesDebrief;
|
|
181
|
+
var commandHeads_1 = require_commandHeads();
|
|
182
|
+
function classifyWorkMilestone(command) {
|
|
183
|
+
if (!command)
|
|
184
|
+
return void 0;
|
|
185
|
+
const value = command.toLowerCase().trim();
|
|
186
|
+
if (!/\bgh\b/.test(value))
|
|
187
|
+
return void 0;
|
|
188
|
+
const heads = (0, commandHeads_1.commandHeads)(value);
|
|
189
|
+
if (heads.some((head) => /^gh\s+pr\s+merge\b/.test(head)))
|
|
190
|
+
return "pr_merged";
|
|
191
|
+
if (heads.some((head) => /^gh\s+issue\s+close\b/.test(head)))
|
|
192
|
+
return "issue_closed";
|
|
193
|
+
if (heads.some((head) => /^gh\s+pr\s+create\b/.test(head)))
|
|
194
|
+
return "pr_opened";
|
|
195
|
+
return void 0;
|
|
196
|
+
}
|
|
197
|
+
function invitesDebrief(kind) {
|
|
198
|
+
return kind === "pr_merged" || kind === "issue_closed";
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
});
|
|
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
|
+
|
|
299
|
+
// ../packages/tool-kit/out/buckets.js
|
|
300
|
+
var require_buckets = __commonJS({
|
|
301
|
+
"../packages/tool-kit/out/buckets.js"(exports) {
|
|
302
|
+
"use strict";
|
|
303
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
304
|
+
exports.bucketLinesChanged = bucketLinesChanged;
|
|
305
|
+
exports.bucketDurationMs = bucketDurationMs2;
|
|
306
|
+
function bucketLinesChanged(count) {
|
|
307
|
+
if (count <= 0)
|
|
308
|
+
return "0";
|
|
309
|
+
if (count <= 10)
|
|
310
|
+
return "1-10";
|
|
311
|
+
if (count <= 50)
|
|
312
|
+
return "10-50";
|
|
313
|
+
if (count <= 200)
|
|
314
|
+
return "50-200";
|
|
315
|
+
return "200+";
|
|
316
|
+
}
|
|
317
|
+
function bucketDurationMs2(durationMs) {
|
|
318
|
+
if (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0)
|
|
319
|
+
return void 0;
|
|
320
|
+
const minutes = durationMs / 6e4;
|
|
321
|
+
if (minutes <= 1)
|
|
322
|
+
return "0-1m";
|
|
323
|
+
if (minutes <= 5)
|
|
324
|
+
return "1-5m";
|
|
325
|
+
if (minutes <= 10)
|
|
326
|
+
return "5-10m";
|
|
327
|
+
if (minutes <= 30)
|
|
328
|
+
return "10-30m";
|
|
329
|
+
if (minutes <= 60)
|
|
330
|
+
return "30-60m";
|
|
331
|
+
return "60m+";
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// ../packages/tool-kit/out/afterHours.js
|
|
337
|
+
var require_afterHours = __commonJS({
|
|
338
|
+
"../packages/tool-kit/out/afterHours.js"(exports) {
|
|
339
|
+
"use strict";
|
|
340
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
341
|
+
exports.BUSINESS_DAY = void 0;
|
|
342
|
+
exports.isOutsideBusinessHours = isOutsideBusinessHours;
|
|
343
|
+
exports.isAfterHours = isAfterHours;
|
|
344
|
+
exports.utcOffsetMinutesAt = utcOffsetMinutesAt;
|
|
345
|
+
exports.localHourAt = localHourAt;
|
|
346
|
+
exports.BUSINESS_DAY = {
|
|
347
|
+
/** 0 = Sunday … 6 = Saturday. */
|
|
348
|
+
days: [1, 2, 3, 4, 5],
|
|
349
|
+
start: "09:00",
|
|
350
|
+
end: "17:00"
|
|
351
|
+
};
|
|
352
|
+
function isOutsideBusinessHours(at = /* @__PURE__ */ new Date(), opts = {}) {
|
|
353
|
+
const days = opts.days ?? exports.BUSINESS_DAY.days;
|
|
354
|
+
if (!days.includes(at.getDay()))
|
|
355
|
+
return true;
|
|
356
|
+
const minutes = at.getHours() * 60 + at.getMinutes();
|
|
357
|
+
const start = parseTimeToMinutes(opts.start ?? exports.BUSINESS_DAY.start, 9 * 60);
|
|
358
|
+
const end = parseTimeToMinutes(opts.end ?? exports.BUSINESS_DAY.end, 17 * 60);
|
|
359
|
+
if (start >= end)
|
|
360
|
+
return false;
|
|
361
|
+
return minutes < start || minutes >= end;
|
|
362
|
+
}
|
|
363
|
+
function isAfterHours(now = /* @__PURE__ */ new Date(), start = "19:00", end = "07:00") {
|
|
364
|
+
const currentMinutes = now.getHours() * 60 + now.getMinutes();
|
|
365
|
+
const startMinutes = parseTimeToMinutes(start, 19 * 60);
|
|
366
|
+
const endMinutes = parseTimeToMinutes(end, 7 * 60);
|
|
367
|
+
if (startMinutes === endMinutes)
|
|
368
|
+
return false;
|
|
369
|
+
if (startMinutes < endMinutes)
|
|
370
|
+
return currentMinutes >= startMinutes && currentMinutes < endMinutes;
|
|
371
|
+
return currentMinutes >= startMinutes || currentMinutes < endMinutes;
|
|
372
|
+
}
|
|
373
|
+
function parseTimeToMinutes(value, fallback) {
|
|
374
|
+
const match = /^(\d{1,2}):(\d{2})$/.exec(value.trim());
|
|
375
|
+
if (!match)
|
|
376
|
+
return fallback;
|
|
377
|
+
const hours = Number(match[1]);
|
|
378
|
+
const minutes = Number(match[2]);
|
|
379
|
+
if (Number.isNaN(hours) || Number.isNaN(minutes))
|
|
380
|
+
return fallback;
|
|
381
|
+
return Math.max(0, Math.min(23, hours)) * 60 + Math.max(0, Math.min(59, minutes));
|
|
382
|
+
}
|
|
383
|
+
function utcOffsetMinutesAt(at) {
|
|
384
|
+
return -at.getTimezoneOffset();
|
|
385
|
+
}
|
|
386
|
+
function localHourAt(at, utcOffsetMinutes) {
|
|
387
|
+
const shifted = new Date(at.getTime() + utcOffsetMinutes * 6e4);
|
|
388
|
+
return shifted.getUTCHours();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
});
|
|
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
|
+
|
|
679
|
+
// ../packages/tool-kit/out/payload.js
|
|
680
|
+
var require_payload = __commonJS({
|
|
681
|
+
"../packages/tool-kit/out/payload.js"(exports) {
|
|
682
|
+
"use strict";
|
|
683
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
684
|
+
exports.mintIdempotencyKey = mintIdempotencyKey;
|
|
685
|
+
exports.getString = getString2;
|
|
686
|
+
exports.getNumber = getNumber2;
|
|
687
|
+
exports.getNested = getNested;
|
|
688
|
+
exports.getNestedString = getNestedString2;
|
|
689
|
+
exports.getNestedNumber = getNestedNumber;
|
|
690
|
+
exports.inferOutcome = inferOutcome2;
|
|
691
|
+
exports.outcomeForHook = outcomeForHook;
|
|
692
|
+
exports.looksLikeCorrection = looksLikeCorrection2;
|
|
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
|
+
}
|
|
701
|
+
function getString2(input, keys) {
|
|
702
|
+
for (const key of keys) {
|
|
703
|
+
const value = input[key];
|
|
704
|
+
if (typeof value === "string" && value.trim())
|
|
705
|
+
return value;
|
|
706
|
+
}
|
|
707
|
+
return void 0;
|
|
708
|
+
}
|
|
709
|
+
function getNumber2(input, keys) {
|
|
710
|
+
for (const key of keys) {
|
|
711
|
+
const value = input[key];
|
|
712
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
713
|
+
return value;
|
|
714
|
+
}
|
|
715
|
+
return void 0;
|
|
716
|
+
}
|
|
717
|
+
function getNested(input, path2) {
|
|
718
|
+
let current = input;
|
|
719
|
+
for (const segment of path2) {
|
|
720
|
+
if (!current || typeof current !== "object")
|
|
721
|
+
return void 0;
|
|
722
|
+
current = current[segment];
|
|
723
|
+
}
|
|
724
|
+
return current;
|
|
725
|
+
}
|
|
726
|
+
function getNestedString2(input, paths) {
|
|
727
|
+
for (const path2 of paths) {
|
|
728
|
+
const value = getNested(input, path2);
|
|
729
|
+
if (typeof value === "string" && value.trim())
|
|
730
|
+
return value;
|
|
731
|
+
}
|
|
732
|
+
return void 0;
|
|
733
|
+
}
|
|
734
|
+
function getNestedNumber(input, paths) {
|
|
735
|
+
for (const path2 of paths) {
|
|
736
|
+
const value = getNested(input, path2);
|
|
737
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
738
|
+
return value;
|
|
739
|
+
}
|
|
740
|
+
return void 0;
|
|
741
|
+
}
|
|
742
|
+
function inferOutcome2(input) {
|
|
743
|
+
const exitCode = getNumber2(input, ["exitCode", "exit_code", "status"]) ?? getNestedNumber(input, [["tool_response", "exitCode"], ["tool_response", "exit_code"], ["result", "exitCode"], ["result", "exit_code"]]);
|
|
744
|
+
if (typeof exitCode === "number")
|
|
745
|
+
return exitCode === 0 ? "success" : "failure";
|
|
746
|
+
const error = getString2(input, ["error", "errorMessage"]) ?? getNestedString2(input, [["tool_response", "error"], ["result", "error"]]);
|
|
747
|
+
if (error)
|
|
748
|
+
return "failure";
|
|
749
|
+
return "unknown";
|
|
750
|
+
}
|
|
751
|
+
function outcomeForHook(hookName, input) {
|
|
752
|
+
if (hookName === "PostToolUseFailure") {
|
|
753
|
+
const interrupted = input["is_interrupt"] === true || getNested(input, ["tool_response", "interrupted"]) === true;
|
|
754
|
+
return interrupted ? "cancelled" : "failure";
|
|
755
|
+
}
|
|
756
|
+
if (hookName === "PostToolUse") {
|
|
757
|
+
if (getNested(input, ["tool_response", "interrupted"]) === true)
|
|
758
|
+
return "cancelled";
|
|
759
|
+
return "success";
|
|
760
|
+
}
|
|
761
|
+
return "unknown";
|
|
762
|
+
}
|
|
763
|
+
function looksLikeCorrection2(text) {
|
|
764
|
+
if (!text)
|
|
765
|
+
return false;
|
|
766
|
+
return /\b(wrong|incorrect|try again|fix|not what i asked|that's not|that is not|redo|regenerate|you missed|doesn't work|does not work)\b/i.test(text);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// ../packages/tool-kit/out/eventLog.js
|
|
772
|
+
var require_eventLog = __commonJS({
|
|
773
|
+
"../packages/tool-kit/out/eventLog.js"(exports) {
|
|
774
|
+
"use strict";
|
|
775
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
776
|
+
if (k2 === void 0) k2 = k;
|
|
777
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
778
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
779
|
+
desc = { enumerable: true, get: function() {
|
|
780
|
+
return m[k];
|
|
781
|
+
} };
|
|
782
|
+
}
|
|
783
|
+
Object.defineProperty(o, k2, desc);
|
|
784
|
+
} : function(o, m, k, k2) {
|
|
785
|
+
if (k2 === void 0) k2 = k;
|
|
786
|
+
o[k2] = m[k];
|
|
787
|
+
});
|
|
788
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
789
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
790
|
+
} : function(o, v) {
|
|
791
|
+
o["default"] = v;
|
|
792
|
+
});
|
|
793
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
794
|
+
var ownKeys = function(o) {
|
|
795
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
796
|
+
var ar = [];
|
|
797
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
798
|
+
return ar;
|
|
799
|
+
};
|
|
800
|
+
return ownKeys(o);
|
|
801
|
+
};
|
|
802
|
+
return function(mod) {
|
|
803
|
+
if (mod && mod.__esModule) return mod;
|
|
804
|
+
var result = {};
|
|
805
|
+
if (mod != null) {
|
|
806
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
807
|
+
}
|
|
808
|
+
__setModuleDefault(result, mod);
|
|
809
|
+
return result;
|
|
810
|
+
};
|
|
811
|
+
}();
|
|
812
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
813
|
+
exports.EVENT_LOG_ENV_VAR = void 0;
|
|
814
|
+
exports.expandUserPath = expandUserPath;
|
|
815
|
+
exports.resolveEventLogPath = resolveEventLogPath;
|
|
816
|
+
exports.appendEventLog = appendEventLog;
|
|
817
|
+
var fs = __importStar(__require("fs"));
|
|
818
|
+
var os2 = __importStar(__require("os"));
|
|
819
|
+
var path2 = __importStar(__require("path"));
|
|
820
|
+
exports.EVENT_LOG_ENV_VAR = "ASCENDA_EVENT_LOG_FILE";
|
|
821
|
+
var MAX_BYTES = 5 * 1024 * 1024;
|
|
822
|
+
function expandUserPath(configured) {
|
|
823
|
+
const value = configured?.trim();
|
|
824
|
+
if (!value)
|
|
825
|
+
return void 0;
|
|
826
|
+
if (value === "~")
|
|
827
|
+
return os2.homedir();
|
|
828
|
+
if (value.startsWith("~/"))
|
|
829
|
+
return path2.join(os2.homedir(), value.slice(2));
|
|
830
|
+
return path2.resolve(value);
|
|
831
|
+
}
|
|
832
|
+
function resolveEventLogPath() {
|
|
833
|
+
return expandUserPath(process.env[exports.EVENT_LOG_ENV_VAR]);
|
|
834
|
+
}
|
|
835
|
+
function appendEventLog(logFilePath, entry) {
|
|
836
|
+
try {
|
|
837
|
+
rotateIfLarge(logFilePath);
|
|
838
|
+
const dir = path2.dirname(logFilePath);
|
|
839
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
840
|
+
fs.appendFileSync(logFilePath, `${JSON.stringify(entry)}
|
|
841
|
+
`, { encoding: "utf8", mode: 384 });
|
|
842
|
+
if (process.platform !== "win32")
|
|
843
|
+
fs.chmodSync(logFilePath, 384);
|
|
844
|
+
} catch {
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
function rotateIfLarge(logFilePath) {
|
|
848
|
+
try {
|
|
849
|
+
if (fs.statSync(logFilePath).size < MAX_BYTES)
|
|
850
|
+
return;
|
|
851
|
+
fs.renameSync(logFilePath, `${logFilePath}.1`);
|
|
852
|
+
} catch {
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
// ../packages/tool-kit/out/http.js
|
|
859
|
+
var require_http = __commonJS({
|
|
860
|
+
"../packages/tool-kit/out/http.js"(exports) {
|
|
861
|
+
"use strict";
|
|
862
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
863
|
+
exports.AscendaApiError = void 0;
|
|
864
|
+
exports.createPairingSession = createPairingSession;
|
|
865
|
+
exports.getPairingStatus = getPairingStatus;
|
|
866
|
+
exports.renewToolToken = renewToolToken;
|
|
867
|
+
exports.isRetryableStatus = isRetryableStatus;
|
|
868
|
+
exports.postToolEvent = postToolEvent;
|
|
869
|
+
exports.postToolEventsBatch = postToolEventsBatch;
|
|
870
|
+
exports.parseIngestResponse = parseIngestResponse;
|
|
871
|
+
var tool_contract_1 = require_out();
|
|
872
|
+
var AscendaApiError = class extends Error {
|
|
873
|
+
status;
|
|
874
|
+
errorCode;
|
|
875
|
+
constructor(status, errorCode, body) {
|
|
876
|
+
super(body ?? `Ascenda API error ${status}`);
|
|
877
|
+
this.status = status;
|
|
878
|
+
this.errorCode = errorCode;
|
|
879
|
+
this.name = "AscendaApiError";
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
exports.AscendaApiError = AscendaApiError;
|
|
883
|
+
async function createPairingSession(apiBaseUrl, toolInstallationId, toolType, displayName) {
|
|
884
|
+
const response = await fetch(`${apiBaseUrl}/v1/tool-pairing-sessions`, {
|
|
885
|
+
method: "POST",
|
|
886
|
+
headers: { "Content-Type": "application/json" },
|
|
887
|
+
body: JSON.stringify({ toolInstallationId, toolType, displayName })
|
|
888
|
+
});
|
|
889
|
+
if (!response.ok)
|
|
890
|
+
throw new AscendaApiError(response.status, void 0, await response.text());
|
|
891
|
+
return await response.json();
|
|
892
|
+
}
|
|
893
|
+
async function getPairingStatus(apiBaseUrl, pairingSessionId) {
|
|
894
|
+
const response = await fetch(`${apiBaseUrl}/v1/tool-pairing-sessions/${encodeURIComponent(pairingSessionId)}/status`, {
|
|
895
|
+
method: "GET",
|
|
896
|
+
headers: { Accept: "application/json" }
|
|
897
|
+
});
|
|
898
|
+
if (!response.ok)
|
|
899
|
+
throw new AscendaApiError(response.status, void 0, await response.text());
|
|
900
|
+
return await response.json();
|
|
901
|
+
}
|
|
902
|
+
async function renewToolToken(apiBaseUrl, eventWriteToken, signal) {
|
|
903
|
+
const response = await fetch(`${apiBaseUrl}/v1/tool-events/renew-token`, {
|
|
904
|
+
method: "POST",
|
|
905
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${eventWriteToken}` },
|
|
906
|
+
signal
|
|
907
|
+
});
|
|
908
|
+
if (response.status === 401)
|
|
909
|
+
return null;
|
|
910
|
+
if (!response.ok)
|
|
911
|
+
throw new AscendaApiError(response.status, void 0, await response.text());
|
|
912
|
+
return await response.json();
|
|
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
|
+
}
|
|
943
|
+
function isRetryableStatus(status) {
|
|
944
|
+
return status === 408 || status === 429 || status !== void 0 && status >= 500 && status <= 599;
|
|
945
|
+
}
|
|
946
|
+
async function postToolEvent(apiBaseUrl, eventWriteToken, payload, signal) {
|
|
947
|
+
return sendIngest(`${apiBaseUrl}/v1/tool-events`, eventWriteToken, JSON.stringify(payload), signal);
|
|
948
|
+
}
|
|
949
|
+
async function postToolEventsBatch(apiBaseUrl, eventWriteToken, payloads, signal) {
|
|
950
|
+
return sendIngest(`${apiBaseUrl}/v1/tool-events/batch`, eventWriteToken, JSON.stringify({ events: payloads }), signal);
|
|
951
|
+
}
|
|
952
|
+
async function sendIngest(url, eventWriteToken, body, signal) {
|
|
953
|
+
try {
|
|
954
|
+
const response = await fetch(url, {
|
|
955
|
+
method: "POST",
|
|
956
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${eventWriteToken}` },
|
|
957
|
+
body,
|
|
958
|
+
signal
|
|
959
|
+
});
|
|
960
|
+
return parseIngestResponse(response);
|
|
961
|
+
} catch (error) {
|
|
962
|
+
return {
|
|
963
|
+
result: "transport_error",
|
|
964
|
+
detail: error instanceof Error ? `${error.name}: ${error.message}` : String(error)
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
async function parseIngestResponse(response) {
|
|
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
|
+
}
|
|
983
|
+
const body = await response.text();
|
|
984
|
+
let errorCode;
|
|
985
|
+
try {
|
|
986
|
+
errorCode = JSON.parse(body).error;
|
|
987
|
+
} catch {
|
|
988
|
+
errorCode = void 0;
|
|
989
|
+
}
|
|
990
|
+
const base = { httpStatus: response.status, errorCode, detail: body || void 0 };
|
|
991
|
+
if (response.status === 401)
|
|
992
|
+
return { ...base, result: "auth_failed" };
|
|
993
|
+
if (response.status === 403 && errorCode === "consent_missing_or_expired")
|
|
994
|
+
return { ...base, result: "consent_missing" };
|
|
995
|
+
if (response.status === 400 || response.status === 422)
|
|
996
|
+
return { ...base, result: "validation_failed" };
|
|
997
|
+
return { ...base, result: "transport_error" };
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
// ../packages/tool-kit/out/tokenStore.js
|
|
1003
|
+
var require_tokenStore = __commonJS({
|
|
1004
|
+
"../packages/tool-kit/out/tokenStore.js"(exports) {
|
|
1005
|
+
"use strict";
|
|
1006
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1007
|
+
if (k2 === void 0) k2 = k;
|
|
1008
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1009
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1010
|
+
desc = { enumerable: true, get: function() {
|
|
1011
|
+
return m[k];
|
|
1012
|
+
} };
|
|
1013
|
+
}
|
|
1014
|
+
Object.defineProperty(o, k2, desc);
|
|
1015
|
+
} : function(o, m, k, k2) {
|
|
1016
|
+
if (k2 === void 0) k2 = k;
|
|
1017
|
+
o[k2] = m[k];
|
|
1018
|
+
});
|
|
1019
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
1020
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1021
|
+
} : function(o, v) {
|
|
1022
|
+
o["default"] = v;
|
|
1023
|
+
});
|
|
1024
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
1025
|
+
var ownKeys = function(o) {
|
|
1026
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1027
|
+
var ar = [];
|
|
1028
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1029
|
+
return ar;
|
|
1030
|
+
};
|
|
1031
|
+
return ownKeys(o);
|
|
1032
|
+
};
|
|
1033
|
+
return function(mod) {
|
|
1034
|
+
if (mod && mod.__esModule) return mod;
|
|
1035
|
+
var result = {};
|
|
1036
|
+
if (mod != null) {
|
|
1037
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1038
|
+
}
|
|
1039
|
+
__setModuleDefault(result, mod);
|
|
1040
|
+
return result;
|
|
1041
|
+
};
|
|
1042
|
+
}();
|
|
1043
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1044
|
+
exports.ascendaHome = ascendaHome;
|
|
1045
|
+
exports.defaultTokenFilePath = defaultTokenFilePath;
|
|
1046
|
+
exports.persistEventWriteToken = persistEventWriteToken;
|
|
1047
|
+
exports.listPersistedToolInstallationIds = listPersistedToolInstallationIds;
|
|
1048
|
+
exports.readTokenFile = readTokenFile;
|
|
1049
|
+
exports.sanitizeFilePart = sanitizeFilePart;
|
|
1050
|
+
var fs = __importStar(__require("fs"));
|
|
1051
|
+
var os2 = __importStar(__require("os"));
|
|
1052
|
+
var path2 = __importStar(__require("path"));
|
|
1053
|
+
function ascendaHome() {
|
|
1054
|
+
return process.env.ASCENDA_HOME ?? path2.join(os2.homedir(), ".ascenda");
|
|
1055
|
+
}
|
|
1056
|
+
function defaultTokenFilePath(toolInstallationId) {
|
|
1057
|
+
return path2.join(ascendaHome(), "tokens", sanitizeFilePart(toolInstallationId));
|
|
1058
|
+
}
|
|
1059
|
+
function persistEventWriteToken(tokenFilePath, token) {
|
|
1060
|
+
const dir = path2.dirname(tokenFilePath);
|
|
1061
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1062
|
+
fs.writeFileSync(tokenFilePath, token, { encoding: "utf8", mode: 384 });
|
|
1063
|
+
if (process.platform !== "win32") {
|
|
1064
|
+
fs.chmodSync(dir, 448);
|
|
1065
|
+
fs.chmodSync(tokenFilePath, 384);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
function listPersistedToolInstallationIds(toolType) {
|
|
1069
|
+
const prefix = `${sanitizeFilePart(toolType)}_`;
|
|
1070
|
+
const dir = path2.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 = path2.join(dir, name);
|
|
1082
|
+
try {
|
|
1083
|
+
if (!fs.statSync(file).isFile())
|
|
1084
|
+
continue;
|
|
1085
|
+
} catch {
|
|
1086
|
+
continue;
|
|
1087
|
+
}
|
|
1088
|
+
if (readTokenFile(file) === void 0)
|
|
1089
|
+
continue;
|
|
1090
|
+
ids.push(`${toolType}:${name.slice(prefix.length)}`);
|
|
1091
|
+
}
|
|
1092
|
+
return ids;
|
|
1093
|
+
}
|
|
1094
|
+
function readTokenFile(tokenFilePath) {
|
|
1095
|
+
try {
|
|
1096
|
+
if (!fs.existsSync(tokenFilePath))
|
|
1097
|
+
return void 0;
|
|
1098
|
+
const value = fs.readFileSync(tokenFilePath, "utf8").trim();
|
|
1099
|
+
return value || void 0;
|
|
1100
|
+
} catch {
|
|
1101
|
+
return void 0;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
function sanitizeFilePart(value) {
|
|
1105
|
+
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
// ../packages/tool-kit/out/stateStore.js
|
|
1111
|
+
var require_stateStore = __commonJS({
|
|
1112
|
+
"../packages/tool-kit/out/stateStore.js"(exports) {
|
|
1113
|
+
"use strict";
|
|
1114
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1115
|
+
if (k2 === void 0) k2 = k;
|
|
1116
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1117
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1118
|
+
desc = { enumerable: true, get: function() {
|
|
1119
|
+
return m[k];
|
|
1120
|
+
} };
|
|
1121
|
+
}
|
|
1122
|
+
Object.defineProperty(o, k2, desc);
|
|
1123
|
+
} : function(o, m, k, k2) {
|
|
1124
|
+
if (k2 === void 0) k2 = k;
|
|
1125
|
+
o[k2] = m[k];
|
|
1126
|
+
});
|
|
1127
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
1128
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1129
|
+
} : function(o, v) {
|
|
1130
|
+
o["default"] = v;
|
|
1131
|
+
});
|
|
1132
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
1133
|
+
var ownKeys = function(o) {
|
|
1134
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1135
|
+
var ar = [];
|
|
1136
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1137
|
+
return ar;
|
|
1138
|
+
};
|
|
1139
|
+
return ownKeys(o);
|
|
1140
|
+
};
|
|
1141
|
+
return function(mod) {
|
|
1142
|
+
if (mod && mod.__esModule) return mod;
|
|
1143
|
+
var result = {};
|
|
1144
|
+
if (mod != null) {
|
|
1145
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1146
|
+
}
|
|
1147
|
+
__setModuleDefault(result, mod);
|
|
1148
|
+
return result;
|
|
1149
|
+
};
|
|
1150
|
+
}();
|
|
1151
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1152
|
+
exports.defaultStateFilePath = defaultStateFilePath;
|
|
1153
|
+
exports.unresolvedToolInstallationId = unresolvedToolInstallationId;
|
|
1154
|
+
exports.unresolvedStateFilePath = unresolvedStateFilePath;
|
|
1155
|
+
exports.readCollectorState = readCollectorState;
|
|
1156
|
+
exports.recordSendOutcome = recordSendOutcome;
|
|
1157
|
+
exports.recordOutboxDiscard = recordOutboxDiscard;
|
|
1158
|
+
exports.shouldAnnounceFailure = shouldAnnounceFailure;
|
|
1159
|
+
exports.markFailureNotified = markFailureNotified;
|
|
1160
|
+
var fs = __importStar(__require("fs"));
|
|
1161
|
+
var os2 = __importStar(__require("os"));
|
|
1162
|
+
var path2 = __importStar(__require("path"));
|
|
1163
|
+
var tokenStore_1 = require_tokenStore();
|
|
1164
|
+
function defaultStateFilePath(toolInstallationId) {
|
|
1165
|
+
const dir = process.env.ASCENDA_STATE_DIR?.trim();
|
|
1166
|
+
const base = dir ? dir : path2.join(os2.homedir(), ".ascenda", "state");
|
|
1167
|
+
return path2.join(base, `${(0, tokenStore_1.sanitizeFilePart)(toolInstallationId)}.json`);
|
|
1168
|
+
}
|
|
1169
|
+
function unresolvedToolInstallationId(toolType) {
|
|
1170
|
+
return `${toolType}:unresolved`;
|
|
1171
|
+
}
|
|
1172
|
+
function unresolvedStateFilePath(toolType) {
|
|
1173
|
+
return defaultStateFilePath(unresolvedToolInstallationId(toolType));
|
|
1174
|
+
}
|
|
1175
|
+
function readCollectorState(stateFilePath) {
|
|
1176
|
+
try {
|
|
1177
|
+
if (!fs.existsSync(stateFilePath))
|
|
1178
|
+
return void 0;
|
|
1179
|
+
const raw = fs.readFileSync(stateFilePath, "utf8").trim();
|
|
1180
|
+
if (!raw)
|
|
1181
|
+
return void 0;
|
|
1182
|
+
const parsed = JSON.parse(raw);
|
|
1183
|
+
if (!parsed || typeof parsed !== "object")
|
|
1184
|
+
return void 0;
|
|
1185
|
+
return parsed;
|
|
1186
|
+
} catch {
|
|
1187
|
+
return void 0;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
function recordSendOutcome(stateFilePath, toolInstallationId, outcome, detail = {}) {
|
|
1191
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1192
|
+
const previous = readCollectorState(stateFilePath);
|
|
1193
|
+
const accepted = outcome === "accepted";
|
|
1194
|
+
const next = {
|
|
1195
|
+
toolInstallationId,
|
|
1196
|
+
lastAttemptAt: now,
|
|
1197
|
+
lastSuccessAt: accepted ? now : previous?.lastSuccessAt,
|
|
1198
|
+
lastOutcome: outcome,
|
|
1199
|
+
consecutiveFailures: accepted ? 0 : (previous?.consecutiveFailures ?? 0) + 1,
|
|
1200
|
+
...detail.httpStatus !== void 0 ? { httpStatus: detail.httpStatus } : {},
|
|
1201
|
+
...detail.errorCode !== void 0 ? { errorCode: detail.errorCode } : {},
|
|
1202
|
+
...detail.detail !== void 0 ? { detail: truncate(detail.detail) } : {},
|
|
1203
|
+
// A success closes the episode; a failure either opens one or continues
|
|
1204
|
+
// the one already open. Carrying `notifiedFailingSince` across a
|
|
1205
|
+
// continuing episode is what keeps the notice to once per outage.
|
|
1206
|
+
...accepted ? {} : { failingSince: previous?.failingSince ?? now },
|
|
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
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
writeStateFile(stateFilePath, next);
|
|
1231
|
+
return next;
|
|
1232
|
+
}
|
|
1233
|
+
function shouldAnnounceFailure(state) {
|
|
1234
|
+
if (!state || state.consecutiveFailures === 0 || !state.failingSince)
|
|
1235
|
+
return false;
|
|
1236
|
+
return state.notifiedFailingSince !== state.failingSince;
|
|
1237
|
+
}
|
|
1238
|
+
function markFailureNotified(stateFilePath, state) {
|
|
1239
|
+
if (!state.failingSince)
|
|
1240
|
+
return;
|
|
1241
|
+
writeStateFile(stateFilePath, { ...state, notifiedFailingSince: state.failingSince });
|
|
1242
|
+
}
|
|
1243
|
+
function writeStateFile(stateFilePath, state) {
|
|
1244
|
+
const temporaryPath = `${stateFilePath}.${process.pid}.tmp`;
|
|
1245
|
+
try {
|
|
1246
|
+
const dir = path2.dirname(stateFilePath);
|
|
1247
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1248
|
+
fs.writeFileSync(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
1249
|
+
`, { encoding: "utf8", mode: 384 });
|
|
1250
|
+
fs.renameSync(temporaryPath, stateFilePath);
|
|
1251
|
+
if (process.platform !== "win32")
|
|
1252
|
+
fs.chmodSync(stateFilePath, 384);
|
|
1253
|
+
} catch {
|
|
1254
|
+
try {
|
|
1255
|
+
if (fs.existsSync(temporaryPath))
|
|
1256
|
+
fs.unlinkSync(temporaryPath);
|
|
1257
|
+
} catch {
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
function truncate(value) {
|
|
1262
|
+
const collapsed = value.replace(/\s+/g, " ").trim();
|
|
1263
|
+
return collapsed.length > 200 ? `${collapsed.slice(0, 197)}...` : collapsed;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
});
|
|
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 path2 = __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 = path2.dirname((0, stateStore_1.defaultStateFilePath)(toolInstallationId));
|
|
1333
|
+
return path2.join(dir, `${(0, tokenStore_1.sanitizeFilePart)(toolInstallationId)}.outbox.jsonl`);
|
|
1334
|
+
}
|
|
1335
|
+
function appendToOutbox(outboxFilePath, payload, now = /* @__PURE__ */ new Date()) {
|
|
1336
|
+
try {
|
|
1337
|
+
const dir = path2.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(path2.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 = path2.dirname(outboxFilePath);
|
|
1453
|
+
const prefix = `${path2.basename(outboxFilePath)}.`;
|
|
1454
|
+
try {
|
|
1455
|
+
return fs.readdirSync(dir).filter((name) => name.startsWith(prefix) && name.includes(CLAIM_SUFFIX)).map((name) => path2.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
|
+
|
|
1488
|
+
// ../packages/tool-kit/out/eventSender.js
|
|
1489
|
+
var require_eventSender = __commonJS({
|
|
1490
|
+
"../packages/tool-kit/out/eventSender.js"(exports) {
|
|
1491
|
+
"use strict";
|
|
1492
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1493
|
+
exports.AscendaEventSender = exports.AscendaSemanticEventError = void 0;
|
|
1494
|
+
exports.buildEventPayload = buildEventPayload;
|
|
1495
|
+
var afterHours_1 = require_afterHours();
|
|
1496
|
+
var tool_contract_1 = require_out();
|
|
1497
|
+
var eventLog_1 = require_eventLog();
|
|
1498
|
+
var http_1 = require_http();
|
|
1499
|
+
var outbox_1 = require_outbox();
|
|
1500
|
+
var tokenStore_1 = require_tokenStore();
|
|
1501
|
+
var stateStore_1 = require_stateStore();
|
|
1502
|
+
var payload_1 = require_payload();
|
|
1503
|
+
var AscendaSemanticEventError = class extends Error {
|
|
1504
|
+
constructor(message) {
|
|
1505
|
+
super(message);
|
|
1506
|
+
this.name = "AscendaSemanticEventError";
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
exports.AscendaSemanticEventError = AscendaSemanticEventError;
|
|
1510
|
+
function buildEventPayload(identity, mapped) {
|
|
1511
|
+
return {
|
|
1512
|
+
toolInstallationId: identity.toolInstallationId,
|
|
1513
|
+
source: identity.source,
|
|
1514
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1515
|
+
idempotencyKey: (0, payload_1.mintIdempotencyKey)(),
|
|
1516
|
+
utcOffsetMinutes: (0, afterHours_1.utcOffsetMinutesAt)(/* @__PURE__ */ new Date()),
|
|
1517
|
+
sessionId: identity.sessionId ?? void 0,
|
|
1518
|
+
workspaceHash: identity.workspaceHash ?? void 0,
|
|
1519
|
+
projectHash: identity.projectHash ?? void 0,
|
|
1520
|
+
consentScope: tool_contract_1.ASCENDA_CONSENT_SCOPE,
|
|
1521
|
+
provenance: tool_contract_1.ASCENDA_PROVENANCE,
|
|
1522
|
+
privacyMode: "metadata_only",
|
|
1523
|
+
...mapped,
|
|
1524
|
+
metadata: mapped.metadata ?? {}
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
1528
|
+
var RETRY_DELAY_MS = 250;
|
|
1529
|
+
var AscendaEventSender = class {
|
|
1530
|
+
config;
|
|
1531
|
+
eventWriteToken;
|
|
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;
|
|
1536
|
+
constructor(config) {
|
|
1537
|
+
this.config = config;
|
|
1538
|
+
this.eventWriteToken = config.eventWriteToken;
|
|
1539
|
+
}
|
|
1540
|
+
async send(mapped) {
|
|
1541
|
+
return this.post(buildEventPayload(this.config, mapped));
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Sends one of the six agent-observed types (dark-flow-gap-analysis §2.1).
|
|
1545
|
+
* Distinct from {@link send} rather than an option on it, because the
|
|
1546
|
+
* differences are non-negotiable, not caller preference:
|
|
1547
|
+
*
|
|
1548
|
+
* - `consentScope`/`provenance` are always the semantic pair — a lease on
|
|
1549
|
+
* `ide_telemetry` alone does not cover these.
|
|
1550
|
+
* - `severity` is always `"low"`. The emitter has no baseline to judge
|
|
1551
|
+
* against; an elevated reading can only come from the backend's own
|
|
1552
|
+
* z-scored evaluation, never from this payload.
|
|
1553
|
+
* - `metadata.skillVersion` is required by the type, not merely
|
|
1554
|
+
* documented, and checked again here in case a caller building the
|
|
1555
|
+
* object dynamically bypasses the type system.
|
|
1556
|
+
*
|
|
1557
|
+
* Rejects locally (never reaches the network) for an eventType outside
|
|
1558
|
+
* {@link SEMANTIC_WORK_SIGNAL_EVENT_TYPES} or a missing/blank
|
|
1559
|
+
* `skillVersion` — a malformed semantic event is a bug in the caller, not
|
|
1560
|
+
* something the backend should have to catch.
|
|
1561
|
+
*/
|
|
1562
|
+
async sendSemanticSignal(mapped) {
|
|
1563
|
+
if (!tool_contract_1.SEMANTIC_WORK_SIGNAL_EVENT_TYPES.includes(mapped.eventType)) {
|
|
1564
|
+
throw new AscendaSemanticEventError(`"${mapped.eventType}" is not a semantic work-signal type. Use send() for a deterministic host event.`);
|
|
1565
|
+
}
|
|
1566
|
+
if (!mapped.metadata.skillVersion || !mapped.metadata.skillVersion.trim()) {
|
|
1567
|
+
throw new AscendaSemanticEventError(`metadata.skillVersion is required for semantic event "${mapped.eventType}".`);
|
|
1568
|
+
}
|
|
1569
|
+
const payload = {
|
|
1570
|
+
toolInstallationId: this.config.toolInstallationId,
|
|
1571
|
+
source: this.config.source,
|
|
1572
|
+
eventType: mapped.eventType,
|
|
1573
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1574
|
+
idempotencyKey: (0, payload_1.mintIdempotencyKey)(),
|
|
1575
|
+
utcOffsetMinutes: (0, afterHours_1.utcOffsetMinutesAt)(/* @__PURE__ */ new Date()),
|
|
1576
|
+
severity: "low",
|
|
1577
|
+
sessionId: this.config.sessionId ?? void 0,
|
|
1578
|
+
workspaceHash: this.config.workspaceHash ?? void 0,
|
|
1579
|
+
projectHash: this.config.projectHash ?? void 0,
|
|
1580
|
+
consentScope: tool_contract_1.ASCENDA_SEMANTIC_CONSENT_SCOPE,
|
|
1581
|
+
provenance: tool_contract_1.ASCENDA_SEMANTIC_PROVENANCE,
|
|
1582
|
+
privacyMode: "metadata_only",
|
|
1583
|
+
metadata: mapped.metadata
|
|
1584
|
+
};
|
|
1585
|
+
return this.post(payload);
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Sends a collaboration event under `workflow_telemetry`.
|
|
1589
|
+
*
|
|
1590
|
+
* A separate method rather than an option on {@link send}, for the same
|
|
1591
|
+
* reason {@link sendSemanticSignal} is: the consent scope is a property of
|
|
1592
|
+
* what the event *is*, and an options bag would let the wrong one be passed
|
|
1593
|
+
* by accident. Rejects locally for anything outside
|
|
1594
|
+
* {@link COLLABORATION_EVENT_TYPES}.
|
|
1595
|
+
*/
|
|
1596
|
+
async sendCollaborationSignal(mapped) {
|
|
1597
|
+
if (!tool_contract_1.COLLABORATION_EVENT_TYPES.includes(mapped.eventType)) {
|
|
1598
|
+
throw new AscendaSemanticEventError(`"${mapped.eventType}" is not a collaboration event type. Use send() for a deterministic host event.`);
|
|
1599
|
+
}
|
|
1600
|
+
const payload = {
|
|
1601
|
+
toolInstallationId: this.config.toolInstallationId,
|
|
1602
|
+
source: this.config.source,
|
|
1603
|
+
eventType: mapped.eventType,
|
|
1604
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1605
|
+
idempotencyKey: (0, payload_1.mintIdempotencyKey)(),
|
|
1606
|
+
utcOffsetMinutes: (0, afterHours_1.utcOffsetMinutesAt)(/* @__PURE__ */ new Date()),
|
|
1607
|
+
severity: "low",
|
|
1608
|
+
sessionId: this.config.sessionId ?? void 0,
|
|
1609
|
+
workspaceHash: this.config.workspaceHash ?? void 0,
|
|
1610
|
+
projectHash: this.config.projectHash ?? void 0,
|
|
1611
|
+
consentScope: tool_contract_1.ASCENDA_COLLABORATION_CONSENT_SCOPE,
|
|
1612
|
+
provenance: tool_contract_1.ASCENDA_PROVENANCE,
|
|
1613
|
+
privacyMode: "metadata_only",
|
|
1614
|
+
metadata: mapped.metadata ?? {}
|
|
1615
|
+
};
|
|
1616
|
+
return this.post(payload);
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* The single choke point every event passes through, and therefore the only
|
|
1620
|
+
* honest place to journal one. Recording here rather than in each adapter is
|
|
1621
|
+
* deliberate: Claude Code, Codex, the GitHub collector and the MCP server all
|
|
1622
|
+
* send through this method, and the defect being fixed showed up in three
|
|
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.
|
|
1630
|
+
*/
|
|
1631
|
+
async post(payload) {
|
|
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
|
+
}
|
|
1642
|
+
this.lastState = (0, stateStore_1.recordSendOutcome)(this.stateFilePath(), this.config.toolInstallationId, outcome.result, {
|
|
1643
|
+
httpStatus: outcome.httpStatus,
|
|
1644
|
+
errorCode: outcome.errorCode,
|
|
1645
|
+
detail: queued ? withNote(outcome.detail, "queued in outbox") : outcome.detail
|
|
1646
|
+
});
|
|
1647
|
+
this.log(payload, outcome.result, queued ? "queued" : void 0);
|
|
1648
|
+
return outcome.result;
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Two recoveries, each tried once. A rejected token gets a renewal and one
|
|
1652
|
+
* replay — rotation is expected and should heal unattended. A transport
|
|
1653
|
+
* error gets one retry, because the common cases (a restarting instance, a
|
|
1654
|
+
* proxy blip, a 429) clear in well under a second and the alternative is
|
|
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.
|
|
1665
|
+
*/
|
|
1666
|
+
async attempt(payload) {
|
|
1667
|
+
const outcome = await (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
1668
|
+
if (outcome.result === "auth_failed") {
|
|
1669
|
+
if (!await this.renewEventToken())
|
|
1670
|
+
return outcome;
|
|
1671
|
+
return (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
1672
|
+
}
|
|
1673
|
+
if (this.isRetryable(outcome)) {
|
|
1674
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
|
1675
|
+
return (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
1676
|
+
}
|
|
1677
|
+
return outcome;
|
|
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
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* The state written by the most recent send, so a caller can decide whether
|
|
1807
|
+
* to surface a one-time notice without re-reading the journal it just wrote.
|
|
1808
|
+
*/
|
|
1809
|
+
get state() {
|
|
1810
|
+
return this.lastState;
|
|
1811
|
+
}
|
|
1812
|
+
/** What this sender's one outbox pass did; undefined before the first send. */
|
|
1813
|
+
get drain() {
|
|
1814
|
+
return this.lastDrain;
|
|
1815
|
+
}
|
|
1816
|
+
stateFilePath() {
|
|
1817
|
+
return this.config.stateFilePath ?? (0, stateStore_1.defaultStateFilePath)(this.config.toolInstallationId);
|
|
1818
|
+
}
|
|
1819
|
+
outboxFilePath() {
|
|
1820
|
+
return this.config.outboxFilePath ?? (0, outbox_1.defaultOutboxFilePath)(this.config.toolInstallationId);
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* Every send path funnels through {@link post}, so semantic and
|
|
1824
|
+
* collaboration signals are logged on the same terms as host events — the
|
|
1825
|
+
* log would be misleading as an audit of what left the machine otherwise.
|
|
1826
|
+
*
|
|
1827
|
+
* An unreachable backend used to be logged as `other` from a catch block.
|
|
1828
|
+
* It is now `transport_error` through the ordinary path, because the
|
|
1829
|
+
* transport returns that outcome instead of throwing.
|
|
1830
|
+
*/
|
|
1831
|
+
log(payload, delivery, outbox) {
|
|
1832
|
+
const logFile = this.config.eventLogFile === void 0 ? (0, eventLog_1.resolveEventLogPath)() : this.config.eventLogFile;
|
|
1833
|
+
if (!logFile)
|
|
1834
|
+
return;
|
|
1835
|
+
(0, eventLog_1.appendEventLog)(logFile, { loggedAt: (/* @__PURE__ */ new Date()).toISOString(), delivery, payload, ...outbox ? { outbox } : {} });
|
|
1836
|
+
}
|
|
1837
|
+
/** Never throws: a renewal that errors is a failed renewal, not a failed turn. */
|
|
1838
|
+
async renewEventToken() {
|
|
1839
|
+
try {
|
|
1840
|
+
const renewed = await (0, http_1.renewToolToken)(this.config.apiBaseUrl, this.eventWriteToken, this.signal());
|
|
1841
|
+
if (!renewed)
|
|
1842
|
+
return false;
|
|
1843
|
+
this.eventWriteToken = renewed.eventWriteToken;
|
|
1844
|
+
(0, tokenStore_1.persistEventWriteToken)(this.config.tokenFilePath, renewed.eventWriteToken);
|
|
1845
|
+
return true;
|
|
1846
|
+
} catch {
|
|
1847
|
+
return false;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
signal() {
|
|
1851
|
+
return AbortSignal.timeout(this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
1852
|
+
}
|
|
1853
|
+
};
|
|
1854
|
+
exports.AscendaEventSender = AscendaEventSender;
|
|
1855
|
+
function withNote(detail, note) {
|
|
1856
|
+
return detail ? `${detail} (${note})` : note;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1861
|
+
// ../packages/tool-kit/out/contextRegistry.js
|
|
1862
|
+
var require_contextRegistry = __commonJS({
|
|
1863
|
+
"../packages/tool-kit/out/contextRegistry.js"(exports) {
|
|
1864
|
+
"use strict";
|
|
1865
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1866
|
+
if (k2 === void 0) k2 = k;
|
|
1867
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1868
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1869
|
+
desc = { enumerable: true, get: function() {
|
|
1870
|
+
return m[k];
|
|
1871
|
+
} };
|
|
1872
|
+
}
|
|
1873
|
+
Object.defineProperty(o, k2, desc);
|
|
1874
|
+
} : function(o, m, k, k2) {
|
|
1875
|
+
if (k2 === void 0) k2 = k;
|
|
1876
|
+
o[k2] = m[k];
|
|
1877
|
+
});
|
|
1878
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
1879
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1880
|
+
} : function(o, v) {
|
|
1881
|
+
o["default"] = v;
|
|
1882
|
+
});
|
|
1883
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
1884
|
+
var ownKeys = function(o) {
|
|
1885
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1886
|
+
var ar = [];
|
|
1887
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1888
|
+
return ar;
|
|
1889
|
+
};
|
|
1890
|
+
return ownKeys(o);
|
|
1891
|
+
};
|
|
1892
|
+
return function(mod) {
|
|
1893
|
+
if (mod && mod.__esModule) return mod;
|
|
1894
|
+
var result = {};
|
|
1895
|
+
if (mod != null) {
|
|
1896
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1897
|
+
}
|
|
1898
|
+
__setModuleDefault(result, mod);
|
|
1899
|
+
return result;
|
|
1900
|
+
};
|
|
1901
|
+
}();
|
|
1902
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1903
|
+
exports.workContextRegistryFilePath = workContextRegistryFilePath;
|
|
1904
|
+
exports.readWorkContextRegistry = readWorkContextRegistry;
|
|
1905
|
+
exports.recordWorkContext = recordWorkContext;
|
|
1906
|
+
exports.recordWorkContextAlias = recordWorkContextAlias;
|
|
1907
|
+
var fs = __importStar(__require("fs"));
|
|
1908
|
+
var os2 = __importStar(__require("os"));
|
|
1909
|
+
var path2 = __importStar(__require("path"));
|
|
1910
|
+
var MAX_PATHS_PER_ENTRY = 8;
|
|
1911
|
+
function workContextRegistryFilePath() {
|
|
1912
|
+
return path2.join(os2.homedir(), ".ascenda", "work-contexts.json");
|
|
1913
|
+
}
|
|
1914
|
+
function readWorkContextRegistry(registryFilePath = workContextRegistryFilePath()) {
|
|
1915
|
+
try {
|
|
1916
|
+
const parsed = JSON.parse(fs.readFileSync(registryFilePath, "utf8"));
|
|
1917
|
+
if (parsed && parsed.version === 1 && parsed.contexts && typeof parsed.contexts === "object") {
|
|
1918
|
+
return parsed;
|
|
1919
|
+
}
|
|
1920
|
+
} catch {
|
|
1921
|
+
}
|
|
1922
|
+
return { version: 1, contexts: {} };
|
|
1923
|
+
}
|
|
1924
|
+
function recordWorkContext(context, options) {
|
|
1925
|
+
if (!context)
|
|
1926
|
+
return false;
|
|
1927
|
+
const updates = [];
|
|
1928
|
+
if (context.projectHash && context.projectLabel) {
|
|
1929
|
+
updates.push({ hash: context.projectHash, kind: "project", label: context.projectLabel, observedPath: context.projectPath ?? context.workspacePath });
|
|
1930
|
+
}
|
|
1931
|
+
if (context.workspaceHash && context.workspaceLabel && context.workspaceHash !== context.projectHash) {
|
|
1932
|
+
updates.push({ hash: context.workspaceHash, kind: "workspace", label: context.workspaceLabel, observedPath: context.workspacePath });
|
|
1933
|
+
}
|
|
1934
|
+
return upsert(updates, options);
|
|
1935
|
+
}
|
|
1936
|
+
function recordWorkContextAlias(hash, label, observedPath, options) {
|
|
1937
|
+
if (!hash || !label)
|
|
1938
|
+
return false;
|
|
1939
|
+
return upsert([{ hash, kind: "alias", label, observedPath: observedPath ?? null }], options);
|
|
1940
|
+
}
|
|
1941
|
+
function upsert(updates, options) {
|
|
1942
|
+
if (updates.length === 0)
|
|
1943
|
+
return false;
|
|
1944
|
+
try {
|
|
1945
|
+
const registryFilePath = options?.registryFilePath ?? workContextRegistryFilePath();
|
|
1946
|
+
const nowIso = (options?.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
1947
|
+
const registry = readWorkContextRegistry(registryFilePath);
|
|
1948
|
+
let dirty = false;
|
|
1949
|
+
for (const update of updates) {
|
|
1950
|
+
const existing = registry.contexts[update.hash];
|
|
1951
|
+
if (!existing) {
|
|
1952
|
+
registry.contexts[update.hash] = {
|
|
1953
|
+
kind: update.kind,
|
|
1954
|
+
label: update.label,
|
|
1955
|
+
paths: update.observedPath ? [update.observedPath] : [],
|
|
1956
|
+
firstSeenAt: nowIso,
|
|
1957
|
+
lastSeenAt: nowIso
|
|
1958
|
+
};
|
|
1959
|
+
dirty = true;
|
|
1960
|
+
continue;
|
|
1961
|
+
}
|
|
1962
|
+
if (existing.kind === "alias" && update.kind !== "alias") {
|
|
1963
|
+
existing.kind = update.kind;
|
|
1964
|
+
dirty = true;
|
|
1965
|
+
}
|
|
1966
|
+
if (existing.label !== update.label && update.kind !== "alias") {
|
|
1967
|
+
existing.label = update.label;
|
|
1968
|
+
dirty = true;
|
|
1969
|
+
}
|
|
1970
|
+
if (update.observedPath && !existing.paths.includes(update.observedPath)) {
|
|
1971
|
+
if (existing.paths.length < MAX_PATHS_PER_ENTRY)
|
|
1972
|
+
existing.paths.push(update.observedPath);
|
|
1973
|
+
dirty = true;
|
|
1974
|
+
}
|
|
1975
|
+
if (dayOf(existing.lastSeenAt) !== dayOf(nowIso)) {
|
|
1976
|
+
existing.lastSeenAt = nowIso;
|
|
1977
|
+
dirty = true;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
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 = path2.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 path2 = __importStar(__require("path"));
|
|
2056
|
+
var tokenStore_1 = require_tokenStore();
|
|
2057
|
+
function credentialsFilePath() {
|
|
2058
|
+
return path2.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(path2.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(path2.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 = forgeProjectHash;
|
|
2156
|
+
exports.parseForgeFullName = parseForgeFullName;
|
|
2157
|
+
exports.readForgeFullName = readForgeFullName;
|
|
2158
|
+
exports.forgeFullNameFromConfig = forgeFullNameFromConfig;
|
|
2159
|
+
exports.recordForgeProjectAlias = recordForgeProjectAlias;
|
|
2160
|
+
var fs = __importStar(__require("fs"));
|
|
2161
|
+
var path2 = __importStar(__require("path"));
|
|
2162
|
+
var contextRegistry_1 = require_contextRegistry();
|
|
2163
|
+
function forgeProjectHash(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(path2.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)
|
|
2247
|
+
return false;
|
|
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 = forgeProjectHash(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;
|
|
2261
|
+
} catch {
|
|
2262
|
+
return false;
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
});
|
|
2267
|
+
|
|
2268
|
+
// ../packages/tool-kit/out/salt.js
|
|
2269
|
+
var require_salt = __commonJS({
|
|
2270
|
+
"../packages/tool-kit/out/salt.js"(exports) {
|
|
2271
|
+
"use strict";
|
|
2272
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
2273
|
+
if (k2 === void 0) k2 = k;
|
|
2274
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
2275
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
2276
|
+
desc = { enumerable: true, get: function() {
|
|
2277
|
+
return m[k];
|
|
2278
|
+
} };
|
|
2279
|
+
}
|
|
2280
|
+
Object.defineProperty(o, k2, desc);
|
|
2281
|
+
} : function(o, m, k, k2) {
|
|
2282
|
+
if (k2 === void 0) k2 = k;
|
|
2283
|
+
o[k2] = m[k];
|
|
2284
|
+
});
|
|
2285
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
2286
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
2287
|
+
} : function(o, v) {
|
|
2288
|
+
o["default"] = v;
|
|
2289
|
+
});
|
|
2290
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
2291
|
+
var ownKeys = function(o) {
|
|
2292
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
2293
|
+
var ar = [];
|
|
2294
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
2295
|
+
return ar;
|
|
2296
|
+
};
|
|
2297
|
+
return ownKeys(o);
|
|
2298
|
+
};
|
|
2299
|
+
return function(mod) {
|
|
2300
|
+
if (mod && mod.__esModule) return mod;
|
|
2301
|
+
var result = {};
|
|
2302
|
+
if (mod != null) {
|
|
2303
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
2304
|
+
}
|
|
2305
|
+
__setModuleDefault(result, mod);
|
|
2306
|
+
return result;
|
|
2307
|
+
};
|
|
2308
|
+
}();
|
|
2309
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2310
|
+
exports.machineSaltFilePath = machineSaltFilePath;
|
|
2311
|
+
exports.readOrCreateMachineSalt = readOrCreateMachineSalt;
|
|
2312
|
+
exports.hashWithMachineSalt = hashWithMachineSalt;
|
|
2313
|
+
var crypto = __importStar(__require("crypto"));
|
|
2314
|
+
var fs = __importStar(__require("fs"));
|
|
2315
|
+
var os2 = __importStar(__require("os"));
|
|
2316
|
+
var path2 = __importStar(__require("path"));
|
|
2317
|
+
function machineSaltFilePath() {
|
|
2318
|
+
return path2.join(os2.homedir(), ".ascenda", "salt");
|
|
2319
|
+
}
|
|
2320
|
+
var cache = /* @__PURE__ */ new Map();
|
|
2321
|
+
function readOrCreateMachineSalt(saltFilePath = machineSaltFilePath()) {
|
|
2322
|
+
const hit = cache.get(saltFilePath);
|
|
2323
|
+
if (hit)
|
|
2324
|
+
return hit;
|
|
2325
|
+
const dir = path2.dirname(saltFilePath);
|
|
2326
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
2327
|
+
let salt;
|
|
2328
|
+
try {
|
|
2329
|
+
salt = crypto.randomBytes(32).toString("hex");
|
|
2330
|
+
fs.writeFileSync(saltFilePath, salt, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
2331
|
+
} catch (error) {
|
|
2332
|
+
if (error.code !== "EEXIST")
|
|
2333
|
+
throw error;
|
|
2334
|
+
salt = fs.readFileSync(saltFilePath, "utf8").trim();
|
|
2335
|
+
}
|
|
2336
|
+
if (process.platform !== "win32") {
|
|
2337
|
+
fs.chmodSync(dir, 448);
|
|
2338
|
+
fs.chmodSync(saltFilePath, 384);
|
|
2339
|
+
}
|
|
2340
|
+
cache.set(saltFilePath, salt);
|
|
2341
|
+
return salt;
|
|
2342
|
+
}
|
|
2343
|
+
function hashWithMachineSalt(value, saltFilePath) {
|
|
2344
|
+
if (!value)
|
|
2345
|
+
return null;
|
|
2346
|
+
return crypto.createHash("sha256").update(readOrCreateMachineSalt(saltFilePath)).update("\0").update(value).digest("hex").slice(0, 16);
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
});
|
|
2350
|
+
|
|
2351
|
+
// ../packages/tool-kit/out/workContext.js
|
|
2352
|
+
var require_workContext = __commonJS({
|
|
2353
|
+
"../packages/tool-kit/out/workContext.js"(exports) {
|
|
2354
|
+
"use strict";
|
|
2355
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
2356
|
+
if (k2 === void 0) k2 = k;
|
|
2357
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
2358
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
2359
|
+
desc = { enumerable: true, get: function() {
|
|
2360
|
+
return m[k];
|
|
2361
|
+
} };
|
|
2362
|
+
}
|
|
2363
|
+
Object.defineProperty(o, k2, desc);
|
|
2364
|
+
} : function(o, m, k, k2) {
|
|
2365
|
+
if (k2 === void 0) k2 = k;
|
|
2366
|
+
o[k2] = m[k];
|
|
2367
|
+
});
|
|
2368
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
2369
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
2370
|
+
} : function(o, v) {
|
|
2371
|
+
o["default"] = v;
|
|
2372
|
+
});
|
|
2373
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
2374
|
+
var ownKeys = function(o) {
|
|
2375
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
2376
|
+
var ar = [];
|
|
2377
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
2378
|
+
return ar;
|
|
2379
|
+
};
|
|
2380
|
+
return ownKeys(o);
|
|
2381
|
+
};
|
|
2382
|
+
return function(mod) {
|
|
2383
|
+
if (mod && mod.__esModule) return mod;
|
|
2384
|
+
var result = {};
|
|
2385
|
+
if (mod != null) {
|
|
2386
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
2387
|
+
}
|
|
2388
|
+
__setModuleDefault(result, mod);
|
|
2389
|
+
return result;
|
|
2390
|
+
};
|
|
2391
|
+
}();
|
|
2392
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2393
|
+
exports.deriveWorkContext = deriveWorkContext;
|
|
2394
|
+
exports.normalizeBranchName = normalizeBranchName;
|
|
2395
|
+
exports.deriveBranchHash = deriveBranchHash;
|
|
2396
|
+
exports.readBranchName = readBranchName;
|
|
2397
|
+
exports.deriveBranchHashForCwd = deriveBranchHashForCwd;
|
|
2398
|
+
var fs = __importStar(__require("fs"));
|
|
2399
|
+
var path2 = __importStar(__require("path"));
|
|
2400
|
+
var salt_1 = require_salt();
|
|
2401
|
+
var MAX_WALK_DEPTH = 64;
|
|
2402
|
+
function deriveWorkContext(cwd, saltFilePath) {
|
|
2403
|
+
if (!cwd || !cwd.trim())
|
|
2404
|
+
return null;
|
|
2405
|
+
const startPath = stripTrailingSeparators(cwd.trim());
|
|
2406
|
+
let roots = null;
|
|
2407
|
+
try {
|
|
2408
|
+
roots = resolveRepositoryRoots(startPath);
|
|
2409
|
+
} catch {
|
|
2410
|
+
roots = null;
|
|
2411
|
+
}
|
|
2412
|
+
if (!roots)
|
|
2413
|
+
roots = inferRootsFromPath(startPath);
|
|
2414
|
+
const workspacePath = roots?.checkoutRoot ?? startPath;
|
|
2415
|
+
const workspaceLabel = basenameOf(workspacePath);
|
|
2416
|
+
if (!workspaceLabel)
|
|
2417
|
+
return null;
|
|
2418
|
+
const projectPath = roots?.canonicalRoot ?? null;
|
|
2419
|
+
const projectLabel = (projectPath ? basenameOf(projectPath) : null) ?? workspaceLabel;
|
|
2420
|
+
return {
|
|
2421
|
+
workspaceLabel,
|
|
2422
|
+
projectLabel,
|
|
2423
|
+
workspaceHash: (0, salt_1.hashWithMachineSalt)(workspaceLabel, saltFilePath),
|
|
2424
|
+
projectHash: (0, salt_1.hashWithMachineSalt)(projectLabel, saltFilePath),
|
|
2425
|
+
workspacePath,
|
|
2426
|
+
projectPath
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
function resolveRepositoryRoots(startDir) {
|
|
2430
|
+
if (!fs.existsSync(startDir))
|
|
2431
|
+
return null;
|
|
2432
|
+
let dir = startDir;
|
|
2433
|
+
for (let depth = 0; depth < MAX_WALK_DEPTH; depth++) {
|
|
2434
|
+
const dotGit = path2.join(dir, ".git");
|
|
2435
|
+
let stat = null;
|
|
2436
|
+
try {
|
|
2437
|
+
stat = fs.statSync(dotGit);
|
|
2438
|
+
} catch {
|
|
2439
|
+
stat = null;
|
|
2440
|
+
}
|
|
2441
|
+
if (stat?.isDirectory())
|
|
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
|
+
}
|
|
2448
|
+
const parent = path2.dirname(dir);
|
|
2449
|
+
if (parent === dir)
|
|
2450
|
+
return null;
|
|
2451
|
+
dir = parent;
|
|
2452
|
+
}
|
|
2453
|
+
return null;
|
|
2454
|
+
}
|
|
2455
|
+
function readGitdirPointer(dotGitFile, containingDir) {
|
|
2456
|
+
try {
|
|
2457
|
+
const match = /^gitdir:\s*(.+)\s*$/m.exec(fs.readFileSync(dotGitFile, "utf8"));
|
|
2458
|
+
if (!match)
|
|
2459
|
+
return null;
|
|
2460
|
+
return path2.resolve(containingDir, match[1].trim());
|
|
2461
|
+
} catch {
|
|
2462
|
+
return null;
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
function worktreeParentRoot(resolvedGitDir) {
|
|
2466
|
+
const marker = `${path2.sep}.git${path2.sep}worktrees${path2.sep}`;
|
|
2467
|
+
const idx = resolvedGitDir.indexOf(marker);
|
|
2468
|
+
if (idx === -1)
|
|
2469
|
+
return null;
|
|
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 join2 = (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: join2(i + 3), canonicalRoot: join2(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: join2(i + 2), canonicalRoot, gitDir: null };
|
|
2492
|
+
}
|
|
2493
|
+
return null;
|
|
2494
|
+
}
|
|
2495
|
+
function stripTrailingSeparators(value) {
|
|
2496
|
+
let end = value.length;
|
|
2497
|
+
while (end > 1 && (value[end - 1] === "/" || value[end - 1] === "\\"))
|
|
2498
|
+
end--;
|
|
2499
|
+
return value.slice(0, end);
|
|
2500
|
+
}
|
|
2501
|
+
function basenameOf(value) {
|
|
2502
|
+
const segment = value.split(/[\\/]/).filter(Boolean).pop() ?? null;
|
|
2503
|
+
return segment && segment.length > 0 ? segment : null;
|
|
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(path2.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
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
});
|
|
2556
|
+
|
|
2557
|
+
// ../packages/tool-kit/out/hookAdapter.js
|
|
2558
|
+
var require_hookAdapter = __commonJS({
|
|
2559
|
+
"../packages/tool-kit/out/hookAdapter.js"(exports) {
|
|
2560
|
+
"use strict";
|
|
2561
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2562
|
+
exports.MissingInstallationIdError = exports.DEFAULT_API_BASE_URL = void 0;
|
|
2563
|
+
exports.resolveCliAgentInstallationId = resolveCliAgentInstallationId;
|
|
2564
|
+
exports.resolveContextHashes = resolveContextHashes;
|
|
2565
|
+
exports.loadCliAgentConfig = loadCliAgentConfig;
|
|
2566
|
+
exports.deliverHookEvents = deliverHookEvents2;
|
|
2567
|
+
var contextRegistry_1 = require_contextRegistry();
|
|
2568
|
+
var credentials_1 = require_credentials();
|
|
2569
|
+
var forgeProject_1 = require_forgeProject();
|
|
2570
|
+
var eventLog_1 = require_eventLog();
|
|
2571
|
+
var eventSender_1 = require_eventSender();
|
|
2572
|
+
var stateStore_1 = require_stateStore();
|
|
2573
|
+
var tokenStore_1 = require_tokenStore();
|
|
2574
|
+
var workContext_1 = require_workContext();
|
|
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
|
+
}
|
|
2606
|
+
function resolveContextHashes(cwd) {
|
|
2607
|
+
const workspaceOverride = process.env.ASCENDA_WORKSPACE_HASH?.trim() || null;
|
|
2608
|
+
const projectOverride = process.env.ASCENDA_PROJECT_HASH?.trim() || null;
|
|
2609
|
+
if (workspaceOverride && projectOverride)
|
|
2610
|
+
return { workspaceHash: workspaceOverride, projectHash: projectOverride };
|
|
2611
|
+
const context = (0, workContext_1.deriveWorkContext)(cwd ?? process.cwd());
|
|
2612
|
+
if (context) {
|
|
2613
|
+
(0, contextRegistry_1.recordWorkContext)(context);
|
|
2614
|
+
(0, forgeProject_1.recordForgeProjectAlias)(context);
|
|
2615
|
+
}
|
|
2616
|
+
return {
|
|
2617
|
+
workspaceHash: workspaceOverride ?? context?.workspaceHash ?? null,
|
|
2618
|
+
projectHash: projectOverride ?? context?.projectHash ?? null
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
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);
|
|
2625
|
+
const tokenFilePath = process.env.ASCENDA_EVENT_WRITE_TOKEN_FILE ?? (0, tokenStore_1.defaultTokenFilePath)(toolInstallationId);
|
|
2626
|
+
const fileToken = (0, tokenStore_1.readTokenFile)(tokenFilePath);
|
|
2627
|
+
const eventWriteToken = fileToken ?? process.env.ASCENDA_EVENT_WRITE_TOKEN;
|
|
2628
|
+
if (!eventWriteToken)
|
|
2629
|
+
throw new Error("Missing ASCENDA_EVENT_WRITE_TOKEN (or token file)");
|
|
2630
|
+
if (!fileToken)
|
|
2631
|
+
(0, tokenStore_1.persistEventWriteToken)(tokenFilePath, eventWriteToken);
|
|
2632
|
+
const contextHashes = resolveContextHashes(cwd);
|
|
2633
|
+
return {
|
|
2634
|
+
apiBaseUrl,
|
|
2635
|
+
toolInstallationId,
|
|
2636
|
+
eventWriteToken,
|
|
2637
|
+
tokenFilePath,
|
|
2638
|
+
sessionId: process.env.ASCENDA_SESSION_ID ?? sessionIdFromHook ?? null,
|
|
2639
|
+
workspaceHash: contextHashes.workspaceHash,
|
|
2640
|
+
projectHash: contextHashes.projectHash,
|
|
2641
|
+
// Agents await command hooks; fail fast rather than stall the user's turn.
|
|
2642
|
+
timeoutMs: parsePositiveInt(process.env.ASCENDA_HTTP_TIMEOUT_MS) ?? 3e3
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2645
|
+
async function deliverHookEvents2(events, options) {
|
|
2646
|
+
if (events.length === 0)
|
|
2647
|
+
return;
|
|
2648
|
+
const notice = options.onNotice ?? ((message) => console.error(message));
|
|
2649
|
+
let config;
|
|
2650
|
+
try {
|
|
2651
|
+
config = loadCliAgentConfig(options.toolType, options.sessionId, options.cwd, options);
|
|
2652
|
+
} catch (error) {
|
|
2653
|
+
if (error instanceof MissingInstallationIdError)
|
|
2654
|
+
journalSkippedSend(options.host, error);
|
|
2655
|
+
const logFile = (0, eventLog_1.resolveEventLogPath)();
|
|
2656
|
+
if (!logFile)
|
|
2657
|
+
throw error;
|
|
2658
|
+
const contextHashes = resolveContextHashes(options.cwd);
|
|
2659
|
+
for (const event of events) {
|
|
2660
|
+
(0, eventLog_1.appendEventLog)(logFile, {
|
|
2661
|
+
loggedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2662
|
+
delivery: "not_sent",
|
|
2663
|
+
payload: (0, eventSender_1.buildEventPayload)({
|
|
2664
|
+
toolInstallationId: `${options.toolType}:unpaired`,
|
|
2665
|
+
source: options.source,
|
|
2666
|
+
sessionId: options.sessionId ?? null,
|
|
2667
|
+
workspaceHash: contextHashes.workspaceHash,
|
|
2668
|
+
projectHash: contextHashes.projectHash
|
|
2669
|
+
}, event)
|
|
2670
|
+
});
|
|
2671
|
+
}
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
const sender = new eventSender_1.AscendaEventSender({
|
|
2675
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
2676
|
+
toolInstallationId: config.toolInstallationId,
|
|
2677
|
+
source: options.source,
|
|
2678
|
+
eventWriteToken: config.eventWriteToken,
|
|
2679
|
+
tokenFilePath: config.tokenFilePath,
|
|
2680
|
+
sessionId: config.sessionId,
|
|
2681
|
+
workspaceHash: config.workspaceHash,
|
|
2682
|
+
projectHash: config.projectHash,
|
|
2683
|
+
timeoutMs: config.timeoutMs
|
|
2684
|
+
});
|
|
2685
|
+
for (const event of events) {
|
|
2686
|
+
const result = await sender.send(event);
|
|
2687
|
+
if (result === "accepted")
|
|
2688
|
+
continue;
|
|
2689
|
+
if (result === "consent_missing") {
|
|
2690
|
+
notice("Ascenda telemetry paused: renew IDE telemetry consent in the Ascenda app.");
|
|
2691
|
+
} else if (result === "auth_failed") {
|
|
2692
|
+
notice("Ascenda telemetry paused: connection revoked or expired. Re-pair via an Ascenda IDE extension or pairing-sim.");
|
|
2693
|
+
} else if (result === "transport_error") {
|
|
2694
|
+
notice("Ascenda telemetry paused: the ingest endpoint could not be reached; the event is kept in the outbox. Your work is unaffected.");
|
|
2695
|
+
} else {
|
|
2696
|
+
notice(`Ascenda telemetry rejected: ${result}`);
|
|
2697
|
+
}
|
|
2698
|
+
return;
|
|
2699
|
+
}
|
|
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
|
+
}
|
|
2707
|
+
function parsePositiveInt(value) {
|
|
2708
|
+
const n = Number(value);
|
|
2709
|
+
return Number.isInteger(n) && n > 0 ? n : void 0;
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
});
|
|
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 = isCliAgentManagementCommand2;
|
|
2757
|
+
exports.cliAgentHookBinPath = cliAgentHookBinPath;
|
|
2758
|
+
exports.runCliAgentSetup = runCliAgentSetup2;
|
|
2759
|
+
exports.writeHookSettings = writeHookSettings;
|
|
2760
|
+
exports.findStaleHookCommands = findStaleHookCommands;
|
|
2761
|
+
var crypto = __importStar(__require("crypto"));
|
|
2762
|
+
var fs = __importStar(__require("fs"));
|
|
2763
|
+
var os2 = __importStar(__require("os"));
|
|
2764
|
+
var path2 = __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 isCliAgentManagementCommand2(argument) {
|
|
2771
|
+
return argument !== void 0 && MANAGEMENT_COMMANDS.has(argument);
|
|
2772
|
+
}
|
|
2773
|
+
function cliAgentHookBinPath(binaryName) {
|
|
2774
|
+
return path2.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 runCliAgentSetup2(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 = path2.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 ${os2.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(path2.dirname(target), { recursive: true });
|
|
2960
|
+
if (path2.resolve(source) !== path2.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(path2.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 ${path2.join((0, tokenStore_1.ascendaHome)(), "tokens")} \u2014 revoke in the Ascenda app to invalidate them`);
|
|
3090
|
+
return 0;
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
});
|
|
3094
|
+
|
|
3095
|
+
// ../packages/tool-kit/out/turnState.js
|
|
3096
|
+
var require_turnState = __commonJS({
|
|
3097
|
+
"../packages/tool-kit/out/turnState.js"(exports) {
|
|
3098
|
+
"use strict";
|
|
3099
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
3100
|
+
if (k2 === void 0) k2 = k;
|
|
3101
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
3102
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
3103
|
+
desc = { enumerable: true, get: function() {
|
|
3104
|
+
return m[k];
|
|
3105
|
+
} };
|
|
3106
|
+
}
|
|
3107
|
+
Object.defineProperty(o, k2, desc);
|
|
3108
|
+
} : function(o, m, k, k2) {
|
|
3109
|
+
if (k2 === void 0) k2 = k;
|
|
3110
|
+
o[k2] = m[k];
|
|
3111
|
+
});
|
|
3112
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
3113
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
3114
|
+
} : function(o, v) {
|
|
3115
|
+
o["default"] = v;
|
|
3116
|
+
});
|
|
3117
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
3118
|
+
var ownKeys = function(o) {
|
|
3119
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
3120
|
+
var ar = [];
|
|
3121
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
3122
|
+
return ar;
|
|
3123
|
+
};
|
|
3124
|
+
return ownKeys(o);
|
|
3125
|
+
};
|
|
3126
|
+
return function(mod) {
|
|
3127
|
+
if (mod && mod.__esModule) return mod;
|
|
3128
|
+
var result = {};
|
|
3129
|
+
if (mod != null) {
|
|
3130
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
3131
|
+
}
|
|
3132
|
+
__setModuleDefault(result, mod);
|
|
3133
|
+
return result;
|
|
3134
|
+
};
|
|
3135
|
+
}();
|
|
3136
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3137
|
+
exports.recordTurnStart = recordTurnStart2;
|
|
3138
|
+
exports.consumeTurnDurationMs = consumeTurnDurationMs2;
|
|
3139
|
+
var fs = __importStar(__require("fs"));
|
|
3140
|
+
var os2 = __importStar(__require("os"));
|
|
3141
|
+
var path2 = __importStar(__require("path"));
|
|
3142
|
+
var stateDir = () => process.env.ASCENDA_STATE_DIR ?? path2.join(os2.homedir(), ".ascenda", "state");
|
|
3143
|
+
function turnFile(agent, sessionId) {
|
|
3144
|
+
return path2.join(stateDir(), `${sanitize(agent)}-turn-${sanitize(sessionId)}`);
|
|
3145
|
+
}
|
|
3146
|
+
function sanitize(value) {
|
|
3147
|
+
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
3148
|
+
}
|
|
3149
|
+
function recordTurnStart2(agent, sessionId, now = Date.now()) {
|
|
3150
|
+
if (!sessionId)
|
|
3151
|
+
return;
|
|
3152
|
+
try {
|
|
3153
|
+
fs.mkdirSync(stateDir(), { recursive: true });
|
|
3154
|
+
fs.writeFileSync(turnFile(agent, sessionId), String(now), { encoding: "utf8", mode: 384 });
|
|
3155
|
+
} catch {
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
function consumeTurnDurationMs2(agent, sessionId, now = Date.now()) {
|
|
3159
|
+
if (!sessionId)
|
|
3160
|
+
return void 0;
|
|
3161
|
+
const file = turnFile(agent, sessionId);
|
|
3162
|
+
try {
|
|
3163
|
+
const started = Number(fs.readFileSync(file, "utf8").trim());
|
|
3164
|
+
fs.rmSync(file, { force: true });
|
|
3165
|
+
if (!Number.isFinite(started) || started <= 0 || started > now)
|
|
3166
|
+
return void 0;
|
|
3167
|
+
return now - started;
|
|
3168
|
+
} catch {
|
|
3169
|
+
return void 0;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
});
|
|
3174
|
+
|
|
3175
|
+
// ../packages/tool-kit/out/liveBus.js
|
|
3176
|
+
var require_liveBus = __commonJS({
|
|
3177
|
+
"../packages/tool-kit/out/liveBus.js"(exports) {
|
|
3178
|
+
"use strict";
|
|
3179
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
3180
|
+
if (k2 === void 0) k2 = k;
|
|
3181
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
3182
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
3183
|
+
desc = { enumerable: true, get: function() {
|
|
3184
|
+
return m[k];
|
|
3185
|
+
} };
|
|
3186
|
+
}
|
|
3187
|
+
Object.defineProperty(o, k2, desc);
|
|
3188
|
+
} : function(o, m, k, k2) {
|
|
3189
|
+
if (k2 === void 0) k2 = k;
|
|
3190
|
+
o[k2] = m[k];
|
|
3191
|
+
});
|
|
3192
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
3193
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
3194
|
+
} : function(o, v) {
|
|
3195
|
+
o["default"] = v;
|
|
3196
|
+
});
|
|
3197
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
3198
|
+
var ownKeys = function(o) {
|
|
3199
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
3200
|
+
var ar = [];
|
|
3201
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
3202
|
+
return ar;
|
|
3203
|
+
};
|
|
3204
|
+
return ownKeys(o);
|
|
3205
|
+
};
|
|
3206
|
+
return function(mod) {
|
|
3207
|
+
if (mod && mod.__esModule) return mod;
|
|
3208
|
+
var result = {};
|
|
3209
|
+
if (mod != null) {
|
|
3210
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
3211
|
+
}
|
|
3212
|
+
__setModuleDefault(result, mod);
|
|
3213
|
+
return result;
|
|
3214
|
+
};
|
|
3215
|
+
}();
|
|
3216
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3217
|
+
exports.liveBusSocketCandidates = liveBusSocketCandidates;
|
|
3218
|
+
exports.liveBusSocketPath = liveBusSocketPath;
|
|
3219
|
+
exports.bucketPromptSize = bucketPromptSize;
|
|
3220
|
+
exports.emitLiveSignal = emitLiveSignal;
|
|
3221
|
+
var fs = __importStar(__require("fs"));
|
|
3222
|
+
var net = __importStar(__require("net"));
|
|
3223
|
+
var os2 = __importStar(__require("os"));
|
|
3224
|
+
var path2 = __importStar(__require("path"));
|
|
3225
|
+
var WRITE_TIMEOUT_MS = 50;
|
|
3226
|
+
var APP_BUNDLE_ID = "one.ascenda.ascendaMissionControl";
|
|
3227
|
+
var SAVER_HOST_BUNDLE_ID = "com.apple.ScreenSaver.Engine.legacyScreenSaver";
|
|
3228
|
+
function liveBusSocketCandidates() {
|
|
3229
|
+
const override = process.env.ASCENDA_LIVE_BUS_SOCKET;
|
|
3230
|
+
if (override)
|
|
3231
|
+
return [override];
|
|
3232
|
+
const home = os2.homedir();
|
|
3233
|
+
return [
|
|
3234
|
+
path2.join(home, ".ascenda", "live.sock"),
|
|
3235
|
+
path2.join(home, "Library", "Containers", APP_BUNDLE_ID, "Data", ".ascenda", "live.sock"),
|
|
3236
|
+
path2.join(home, "Library", "Containers", SAVER_HOST_BUNDLE_ID, "Data", "l.sock")
|
|
3237
|
+
];
|
|
3238
|
+
}
|
|
3239
|
+
function liveBusSocketPath() {
|
|
3240
|
+
const candidates = liveBusSocketCandidates();
|
|
3241
|
+
for (const candidate of candidates) {
|
|
3242
|
+
try {
|
|
3243
|
+
if (fs.statSync(candidate).isSocket())
|
|
3244
|
+
return candidate;
|
|
3245
|
+
} catch {
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
return candidates[0];
|
|
3249
|
+
}
|
|
3250
|
+
function bucketPromptSize(text) {
|
|
3251
|
+
const length = typeof text === "string" ? text.length : 0;
|
|
3252
|
+
if (length <= 280)
|
|
3253
|
+
return "s";
|
|
3254
|
+
if (length <= 2e3)
|
|
3255
|
+
return "m";
|
|
3256
|
+
if (length <= 8e3)
|
|
3257
|
+
return "l";
|
|
3258
|
+
return "xl";
|
|
3259
|
+
}
|
|
3260
|
+
function emitLiveSignal(signal) {
|
|
3261
|
+
return new Promise((resolve) => {
|
|
3262
|
+
let settled = false;
|
|
3263
|
+
const done = () => {
|
|
3264
|
+
if (settled)
|
|
3265
|
+
return;
|
|
3266
|
+
settled = true;
|
|
3267
|
+
try {
|
|
3268
|
+
socket.destroy();
|
|
3269
|
+
} catch {
|
|
3270
|
+
}
|
|
3271
|
+
resolve();
|
|
3272
|
+
};
|
|
3273
|
+
let socket;
|
|
3274
|
+
try {
|
|
3275
|
+
socket = net.createConnection(liveBusSocketPath());
|
|
3276
|
+
} catch {
|
|
3277
|
+
resolve();
|
|
3278
|
+
return;
|
|
3279
|
+
}
|
|
3280
|
+
const timer = setTimeout(done, WRITE_TIMEOUT_MS);
|
|
3281
|
+
if (typeof timer.unref === "function")
|
|
3282
|
+
timer.unref();
|
|
3283
|
+
if (typeof socket.unref === "function")
|
|
3284
|
+
socket.unref();
|
|
3285
|
+
socket.on("error", done);
|
|
3286
|
+
socket.on("connect", () => {
|
|
3287
|
+
try {
|
|
3288
|
+
socket.write(`${JSON.stringify(signal)}
|
|
3289
|
+
`, () => {
|
|
3290
|
+
clearTimeout(timer);
|
|
3291
|
+
done();
|
|
3292
|
+
});
|
|
3293
|
+
} catch {
|
|
3294
|
+
clearTimeout(timer);
|
|
3295
|
+
done();
|
|
3296
|
+
}
|
|
3297
|
+
});
|
|
3298
|
+
});
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
});
|
|
3302
|
+
|
|
3303
|
+
// ../packages/tool-kit/out/index.js
|
|
3304
|
+
var require_out2 = __commonJS({
|
|
3305
|
+
"../packages/tool-kit/out/index.js"(exports) {
|
|
3306
|
+
"use strict";
|
|
3307
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
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;
|
|
3311
|
+
var commandClassifier_1 = require_commandClassifier();
|
|
3312
|
+
Object.defineProperty(exports, "classifyCommand", { enumerable: true, get: function() {
|
|
3313
|
+
return commandClassifier_1.classifyCommand;
|
|
3314
|
+
} });
|
|
3315
|
+
Object.defineProperty(exports, "isVerificationCommand", { enumerable: true, get: function() {
|
|
3316
|
+
return commandClassifier_1.isVerificationCommand;
|
|
3317
|
+
} });
|
|
3318
|
+
var gitActionClassifier_1 = require_gitActionClassifier();
|
|
3319
|
+
Object.defineProperty(exports, "classifyGitAction", { enumerable: true, get: function() {
|
|
3320
|
+
return gitActionClassifier_1.classifyGitAction;
|
|
3321
|
+
} });
|
|
3322
|
+
Object.defineProperty(exports, "isReworkGitAction", { enumerable: true, get: function() {
|
|
3323
|
+
return gitActionClassifier_1.isReworkGitAction;
|
|
3324
|
+
} });
|
|
3325
|
+
var workMilestoneClassifier_1 = require_workMilestoneClassifier();
|
|
3326
|
+
Object.defineProperty(exports, "classifyWorkMilestone", { enumerable: true, get: function() {
|
|
3327
|
+
return workMilestoneClassifier_1.classifyWorkMilestone;
|
|
3328
|
+
} });
|
|
3329
|
+
Object.defineProperty(exports, "invitesDebrief", { enumerable: true, get: function() {
|
|
3330
|
+
return workMilestoneClassifier_1.invitesDebrief;
|
|
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
|
+
} });
|
|
3340
|
+
var buckets_1 = require_buckets();
|
|
3341
|
+
Object.defineProperty(exports, "bucketLinesChanged", { enumerable: true, get: function() {
|
|
3342
|
+
return buckets_1.bucketLinesChanged;
|
|
3343
|
+
} });
|
|
3344
|
+
Object.defineProperty(exports, "bucketDurationMs", { enumerable: true, get: function() {
|
|
3345
|
+
return buckets_1.bucketDurationMs;
|
|
3346
|
+
} });
|
|
3347
|
+
var afterHours_1 = require_afterHours();
|
|
3348
|
+
Object.defineProperty(exports, "isAfterHours", { enumerable: true, get: function() {
|
|
3349
|
+
return afterHours_1.isAfterHours;
|
|
3350
|
+
} });
|
|
3351
|
+
Object.defineProperty(exports, "isOutsideBusinessHours", { enumerable: true, get: function() {
|
|
3352
|
+
return afterHours_1.isOutsideBusinessHours;
|
|
3353
|
+
} });
|
|
3354
|
+
Object.defineProperty(exports, "BUSINESS_DAY", { enumerable: true, get: function() {
|
|
3355
|
+
return afterHours_1.BUSINESS_DAY;
|
|
3356
|
+
} });
|
|
3357
|
+
Object.defineProperty(exports, "utcOffsetMinutesAt", { enumerable: true, get: function() {
|
|
3358
|
+
return afterHours_1.utcOffsetMinutesAt;
|
|
3359
|
+
} });
|
|
3360
|
+
Object.defineProperty(exports, "localHourAt", { enumerable: true, get: function() {
|
|
3361
|
+
return afterHours_1.localHourAt;
|
|
3362
|
+
} });
|
|
3363
|
+
var payload_1 = require_payload();
|
|
3364
|
+
Object.defineProperty(exports, "getString", { enumerable: true, get: function() {
|
|
3365
|
+
return payload_1.getString;
|
|
3366
|
+
} });
|
|
3367
|
+
Object.defineProperty(exports, "getNumber", { enumerable: true, get: function() {
|
|
3368
|
+
return payload_1.getNumber;
|
|
3369
|
+
} });
|
|
3370
|
+
Object.defineProperty(exports, "getNested", { enumerable: true, get: function() {
|
|
3371
|
+
return payload_1.getNested;
|
|
3372
|
+
} });
|
|
3373
|
+
Object.defineProperty(exports, "getNestedString", { enumerable: true, get: function() {
|
|
3374
|
+
return payload_1.getNestedString;
|
|
3375
|
+
} });
|
|
3376
|
+
Object.defineProperty(exports, "getNestedNumber", { enumerable: true, get: function() {
|
|
3377
|
+
return payload_1.getNestedNumber;
|
|
3378
|
+
} });
|
|
3379
|
+
Object.defineProperty(exports, "inferOutcome", { enumerable: true, get: function() {
|
|
3380
|
+
return payload_1.inferOutcome;
|
|
3381
|
+
} });
|
|
3382
|
+
Object.defineProperty(exports, "outcomeForHook", { enumerable: true, get: function() {
|
|
3383
|
+
return payload_1.outcomeForHook;
|
|
3384
|
+
} });
|
|
3385
|
+
Object.defineProperty(exports, "looksLikeCorrection", { enumerable: true, get: function() {
|
|
3386
|
+
return payload_1.looksLikeCorrection;
|
|
3387
|
+
} });
|
|
3388
|
+
Object.defineProperty(exports, "mintIdempotencyKey", { enumerable: true, get: function() {
|
|
3389
|
+
return payload_1.mintIdempotencyKey;
|
|
3390
|
+
} });
|
|
3391
|
+
var eventSender_1 = require_eventSender();
|
|
3392
|
+
Object.defineProperty(exports, "AscendaEventSender", { enumerable: true, get: function() {
|
|
3393
|
+
return eventSender_1.AscendaEventSender;
|
|
3394
|
+
} });
|
|
3395
|
+
Object.defineProperty(exports, "AscendaSemanticEventError", { enumerable: true, get: function() {
|
|
3396
|
+
return eventSender_1.AscendaSemanticEventError;
|
|
3397
|
+
} });
|
|
3398
|
+
Object.defineProperty(exports, "buildEventPayload", { enumerable: true, get: function() {
|
|
3399
|
+
return eventSender_1.buildEventPayload;
|
|
3400
|
+
} });
|
|
3401
|
+
var eventLog_1 = require_eventLog();
|
|
3402
|
+
Object.defineProperty(exports, "EVENT_LOG_ENV_VAR", { enumerable: true, get: function() {
|
|
3403
|
+
return eventLog_1.EVENT_LOG_ENV_VAR;
|
|
3404
|
+
} });
|
|
3405
|
+
Object.defineProperty(exports, "appendEventLog", { enumerable: true, get: function() {
|
|
3406
|
+
return eventLog_1.appendEventLog;
|
|
3407
|
+
} });
|
|
3408
|
+
Object.defineProperty(exports, "expandUserPath", { enumerable: true, get: function() {
|
|
3409
|
+
return eventLog_1.expandUserPath;
|
|
3410
|
+
} });
|
|
3411
|
+
Object.defineProperty(exports, "resolveEventLogPath", { enumerable: true, get: function() {
|
|
3412
|
+
return eventLog_1.resolveEventLogPath;
|
|
3413
|
+
} });
|
|
3414
|
+
var hookAdapter_1 = require_hookAdapter();
|
|
3415
|
+
Object.defineProperty(exports, "DEFAULT_API_BASE_URL", { enumerable: true, get: function() {
|
|
3416
|
+
return hookAdapter_1.DEFAULT_API_BASE_URL;
|
|
3417
|
+
} });
|
|
3418
|
+
Object.defineProperty(exports, "MissingInstallationIdError", { enumerable: true, get: function() {
|
|
3419
|
+
return hookAdapter_1.MissingInstallationIdError;
|
|
3420
|
+
} });
|
|
3421
|
+
Object.defineProperty(exports, "deliverHookEvents", { enumerable: true, get: function() {
|
|
3422
|
+
return hookAdapter_1.deliverHookEvents;
|
|
3423
|
+
} });
|
|
3424
|
+
Object.defineProperty(exports, "loadCliAgentConfig", { enumerable: true, get: function() {
|
|
3425
|
+
return hookAdapter_1.loadCliAgentConfig;
|
|
3426
|
+
} });
|
|
3427
|
+
Object.defineProperty(exports, "resolveCliAgentInstallationId", { enumerable: true, get: function() {
|
|
3428
|
+
return hookAdapter_1.resolveCliAgentInstallationId;
|
|
3429
|
+
} });
|
|
3430
|
+
Object.defineProperty(exports, "resolveContextHashes", { enumerable: true, get: function() {
|
|
3431
|
+
return hookAdapter_1.resolveContextHashes;
|
|
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
|
+
} });
|
|
3471
|
+
var turnState_1 = require_turnState();
|
|
3472
|
+
Object.defineProperty(exports, "consumeTurnDurationMs", { enumerable: true, get: function() {
|
|
3473
|
+
return turnState_1.consumeTurnDurationMs;
|
|
3474
|
+
} });
|
|
3475
|
+
Object.defineProperty(exports, "recordTurnStart", { enumerable: true, get: function() {
|
|
3476
|
+
return turnState_1.recordTurnStart;
|
|
3477
|
+
} });
|
|
3478
|
+
var tokenStore_1 = require_tokenStore();
|
|
3479
|
+
Object.defineProperty(exports, "ascendaHome", { enumerable: true, get: function() {
|
|
3480
|
+
return tokenStore_1.ascendaHome;
|
|
3481
|
+
} });
|
|
3482
|
+
Object.defineProperty(exports, "defaultTokenFilePath", { enumerable: true, get: function() {
|
|
3483
|
+
return tokenStore_1.defaultTokenFilePath;
|
|
3484
|
+
} });
|
|
3485
|
+
Object.defineProperty(exports, "listPersistedToolInstallationIds", { enumerable: true, get: function() {
|
|
3486
|
+
return tokenStore_1.listPersistedToolInstallationIds;
|
|
3487
|
+
} });
|
|
3488
|
+
Object.defineProperty(exports, "persistEventWriteToken", { enumerable: true, get: function() {
|
|
3489
|
+
return tokenStore_1.persistEventWriteToken;
|
|
3490
|
+
} });
|
|
3491
|
+
Object.defineProperty(exports, "readTokenFile", { enumerable: true, get: function() {
|
|
3492
|
+
return tokenStore_1.readTokenFile;
|
|
3493
|
+
} });
|
|
3494
|
+
var stateStore_1 = require_stateStore();
|
|
3495
|
+
Object.defineProperty(exports, "defaultStateFilePath", { enumerable: true, get: function() {
|
|
3496
|
+
return stateStore_1.defaultStateFilePath;
|
|
3497
|
+
} });
|
|
3498
|
+
Object.defineProperty(exports, "readCollectorState", { enumerable: true, get: function() {
|
|
3499
|
+
return stateStore_1.readCollectorState;
|
|
3500
|
+
} });
|
|
3501
|
+
Object.defineProperty(exports, "recordSendOutcome", { enumerable: true, get: function() {
|
|
3502
|
+
return stateStore_1.recordSendOutcome;
|
|
3503
|
+
} });
|
|
3504
|
+
Object.defineProperty(exports, "shouldAnnounceFailure", { enumerable: true, get: function() {
|
|
3505
|
+
return stateStore_1.shouldAnnounceFailure;
|
|
3506
|
+
} });
|
|
3507
|
+
Object.defineProperty(exports, "markFailureNotified", { enumerable: true, get: function() {
|
|
3508
|
+
return stateStore_1.markFailureNotified;
|
|
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
|
+
} });
|
|
3550
|
+
var salt_1 = require_salt();
|
|
3551
|
+
Object.defineProperty(exports, "machineSaltFilePath", { enumerable: true, get: function() {
|
|
3552
|
+
return salt_1.machineSaltFilePath;
|
|
3553
|
+
} });
|
|
3554
|
+
Object.defineProperty(exports, "readOrCreateMachineSalt", { enumerable: true, get: function() {
|
|
3555
|
+
return salt_1.readOrCreateMachineSalt;
|
|
3556
|
+
} });
|
|
3557
|
+
Object.defineProperty(exports, "hashWithMachineSalt", { enumerable: true, get: function() {
|
|
3558
|
+
return salt_1.hashWithMachineSalt;
|
|
3559
|
+
} });
|
|
3560
|
+
var workContext_1 = require_workContext();
|
|
3561
|
+
Object.defineProperty(exports, "deriveWorkContext", { enumerable: true, get: function() {
|
|
3562
|
+
return workContext_1.deriveWorkContext;
|
|
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
|
+
} });
|
|
3576
|
+
var contextRegistry_1 = require_contextRegistry();
|
|
3577
|
+
Object.defineProperty(exports, "recordWorkContext", { enumerable: true, get: function() {
|
|
3578
|
+
return contextRegistry_1.recordWorkContext;
|
|
3579
|
+
} });
|
|
3580
|
+
Object.defineProperty(exports, "recordWorkContextAlias", { enumerable: true, get: function() {
|
|
3581
|
+
return contextRegistry_1.recordWorkContextAlias;
|
|
3582
|
+
} });
|
|
3583
|
+
Object.defineProperty(exports, "readWorkContextRegistry", { enumerable: true, get: function() {
|
|
3584
|
+
return contextRegistry_1.readWorkContextRegistry;
|
|
3585
|
+
} });
|
|
3586
|
+
Object.defineProperty(exports, "workContextRegistryFilePath", { enumerable: true, get: function() {
|
|
3587
|
+
return contextRegistry_1.workContextRegistryFilePath;
|
|
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
|
+
} });
|
|
3605
|
+
var liveBus_1 = require_liveBus();
|
|
3606
|
+
Object.defineProperty(exports, "emitLiveSignal", { enumerable: true, get: function() {
|
|
3607
|
+
return liveBus_1.emitLiveSignal;
|
|
3608
|
+
} });
|
|
3609
|
+
Object.defineProperty(exports, "bucketPromptSize", { enumerable: true, get: function() {
|
|
3610
|
+
return liveBus_1.bucketPromptSize;
|
|
3611
|
+
} });
|
|
3612
|
+
Object.defineProperty(exports, "liveBusSocketPath", { enumerable: true, get: function() {
|
|
3613
|
+
return liveBus_1.liveBusSocketPath;
|
|
3614
|
+
} });
|
|
3615
|
+
Object.defineProperty(exports, "liveBusSocketCandidates", { enumerable: true, get: function() {
|
|
3616
|
+
return liveBus_1.liveBusSocketCandidates;
|
|
3617
|
+
} });
|
|
3618
|
+
var http_1 = require_http();
|
|
3619
|
+
Object.defineProperty(exports, "AscendaApiError", { enumerable: true, get: function() {
|
|
3620
|
+
return http_1.AscendaApiError;
|
|
3621
|
+
} });
|
|
3622
|
+
Object.defineProperty(exports, "createPairingSession", { enumerable: true, get: function() {
|
|
3623
|
+
return http_1.createPairingSession;
|
|
3624
|
+
} });
|
|
3625
|
+
Object.defineProperty(exports, "getPairingStatus", { enumerable: true, get: function() {
|
|
3626
|
+
return http_1.getPairingStatus;
|
|
3627
|
+
} });
|
|
3628
|
+
Object.defineProperty(exports, "renewToolToken", { enumerable: true, get: function() {
|
|
3629
|
+
return http_1.renewToolToken;
|
|
3630
|
+
} });
|
|
3631
|
+
Object.defineProperty(exports, "postToolEvent", { enumerable: true, get: function() {
|
|
3632
|
+
return http_1.postToolEvent;
|
|
3633
|
+
} });
|
|
3634
|
+
Object.defineProperty(exports, "postToolEventsBatch", { enumerable: true, get: function() {
|
|
3635
|
+
return http_1.postToolEventsBatch;
|
|
3636
|
+
} });
|
|
3637
|
+
Object.defineProperty(exports, "parseIngestResponse", { enumerable: true, get: function() {
|
|
3638
|
+
return http_1.parseIngestResponse;
|
|
3639
|
+
} });
|
|
3640
|
+
Object.defineProperty(exports, "isRetryableStatus", { enumerable: true, get: function() {
|
|
3641
|
+
return http_1.isRetryableStatus;
|
|
3642
|
+
} });
|
|
3643
|
+
}
|
|
3644
|
+
});
|
|
3645
|
+
|
|
3646
|
+
// src/cli.ts
|
|
3647
|
+
var import_tool_kit2 = __toESM(require_out2(), 1);
|
|
3648
|
+
|
|
3649
|
+
// src/mapCursorEvent.ts
|
|
3650
|
+
var import_tool_kit = __toESM(require_out2(), 1);
|
|
3651
|
+
|
|
3652
|
+
// src/types.ts
|
|
3653
|
+
var ASCENDA_TOOL_TYPE = "cli_agent";
|
|
3654
|
+
var CURSOR_HOST = "cursor";
|
|
3655
|
+
|
|
3656
|
+
// src/mapCursorEvent.ts
|
|
3657
|
+
function mapCursorEvent(hookName, input, turnDurationMs) {
|
|
3658
|
+
switch (hookName) {
|
|
3659
|
+
case "sessionStart":
|
|
3660
|
+
return [{ eventType: "create_focus_session", severity: "low", metadata: withHost({ activity: "session_started" }) }];
|
|
3661
|
+
case "sessionEnd":
|
|
3662
|
+
return [{ eventType: "recovery_offline_period", severity: "low", metadata: withHost({ activity: "session_ended" }) }];
|
|
3663
|
+
case "beforeSubmitPrompt":
|
|
3664
|
+
return mapPrompt(input);
|
|
3665
|
+
case "preToolUse":
|
|
3666
|
+
return [{ eventType: "ai_tool_call_started", severity: "low", metadata: withHost({ toolName: sanitiseToolName(getToolName(input)) }) }];
|
|
3667
|
+
case "postToolUse":
|
|
3668
|
+
return mapPostToolUse(input);
|
|
3669
|
+
case "postToolUseFailure":
|
|
3670
|
+
return mapPostToolUseFailure(input);
|
|
3671
|
+
case "preCompact":
|
|
3672
|
+
return mapPreCompact(input);
|
|
3673
|
+
case "stop":
|
|
3674
|
+
return mapStop(turnDurationMs);
|
|
3675
|
+
// Deliberately unmapped. The shell / MCP / file-edit hooks are specialised
|
|
3676
|
+
// views of tool calls that preToolUse and postToolUse already report, so
|
|
3677
|
+
// registering them would double-count every command and edit. The rest
|
|
3678
|
+
// (subagent lifecycle, agent thoughts, Tab completions, workspaceOpen)
|
|
3679
|
+
// have no catalog counterpart.
|
|
3680
|
+
default:
|
|
3681
|
+
return [];
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
function mapPrompt(input) {
|
|
3685
|
+
const prompt = (0, import_tool_kit.getString)(input, ["prompt"]);
|
|
3686
|
+
const events = [{ eventType: "ai_prompt_submitted", severity: "low", metadata: withHost({ promptClass: "unknown" }) }];
|
|
3687
|
+
if ((0, import_tool_kit.looksLikeCorrection)(prompt)) {
|
|
3688
|
+
events.push({ eventType: "ai_correction_prompt", severity: "medium", metadata: withHost({ reason: "repeated_reprompting", trigger: "inferred" }) });
|
|
3689
|
+
}
|
|
3690
|
+
return events;
|
|
3691
|
+
}
|
|
3692
|
+
function mapPostToolUse(input) {
|
|
3693
|
+
const toolName = getToolName(input);
|
|
3694
|
+
const safeToolName = sanitiseToolName(toolName);
|
|
3695
|
+
const commandClass = (0, import_tool_kit.classifyCommand)(getCommand(input));
|
|
3696
|
+
const durationBucket = (0, import_tool_kit.bucketDurationMs)((0, import_tool_kit.getNumber)(input, ["duration"]));
|
|
3697
|
+
const outcome = (0, import_tool_kit.inferOutcome)(withParsedToolOutput(input));
|
|
3698
|
+
if (outcome === "failure") {
|
|
3699
|
+
if (isShell(toolName) && (0, import_tool_kit.isVerificationCommand)(commandClass)) {
|
|
3700
|
+
return [{ eventType: "compile_error", severity: "medium", metadata: withHost({ toolName: safeToolName, commandClass, outcome, durationBucket, reason: "test_failure" }) }];
|
|
3701
|
+
}
|
|
3702
|
+
return [{ eventType: "ai_tool_call_failed", severity: "medium", metadata: withHost({ toolName: safeToolName, commandClass, outcome, durationBucket, reason: "tool_failure" }) }];
|
|
3703
|
+
}
|
|
3704
|
+
if (isWriteTool(toolName)) {
|
|
3705
|
+
return [{ eventType: toolName?.toLowerCase() === "write" ? "ai_file_write" : "ai_file_edit", severity: "low", metadata: withHost({ toolName: safeToolName, outcome, durationBucket }) }];
|
|
3706
|
+
}
|
|
3707
|
+
if (isShell(toolName) && (0, import_tool_kit.isVerificationCommand)(commandClass)) {
|
|
3708
|
+
return [{ eventType: "editor_verification_activity", severity: "low", metadata: withHost({ toolName: safeToolName, commandClass, outcome, durationBucket, activity: "ai_test_or_build_run" }) }];
|
|
3709
|
+
}
|
|
3710
|
+
return [{ eventType: "ai_tool_call_completed", severity: "low", metadata: withHost({ toolName: safeToolName, commandClass, outcome, durationBucket }) }];
|
|
3711
|
+
}
|
|
3712
|
+
function mapPostToolUseFailure(input) {
|
|
3713
|
+
const toolName = getToolName(input);
|
|
3714
|
+
const safeToolName = sanitiseToolName(toolName);
|
|
3715
|
+
const commandClass = (0, import_tool_kit.classifyCommand)(getCommand(input));
|
|
3716
|
+
const durationBucket = (0, import_tool_kit.bucketDurationMs)((0, import_tool_kit.getNumber)(input, ["duration"]));
|
|
3717
|
+
const interrupted = input.is_interrupt === true;
|
|
3718
|
+
const outcome = interrupted ? "cancelled" : "failure";
|
|
3719
|
+
if (!interrupted && isShell(toolName) && (0, import_tool_kit.isVerificationCommand)(commandClass)) {
|
|
3720
|
+
return [{ eventType: "compile_error", severity: "medium", metadata: withHost({ toolName: safeToolName, commandClass, outcome, durationBucket, reason: "test_failure" }) }];
|
|
3721
|
+
}
|
|
3722
|
+
return [{
|
|
3723
|
+
eventType: "ai_tool_call_failed",
|
|
3724
|
+
severity: interrupted ? "low" : "medium",
|
|
3725
|
+
metadata: withHost({ toolName: safeToolName, commandClass, outcome, durationBucket, reason: interrupted ? "manual_interrupt" : "tool_failure" })
|
|
3726
|
+
}];
|
|
3727
|
+
}
|
|
3728
|
+
function mapPreCompact(input) {
|
|
3729
|
+
const isManual = (0, import_tool_kit.getString)(input, ["trigger"])?.toLowerCase().includes("manual") ?? false;
|
|
3730
|
+
return [{
|
|
3731
|
+
eventType: isManual ? "context_compression_manual" : "context_compression_auto",
|
|
3732
|
+
severity: isManual ? "medium" : "high",
|
|
3733
|
+
metadata: withHost({ trigger: isManual ? "manual" : "auto", reason: "context_limit", ...contextOccupancy(input) })
|
|
3734
|
+
}];
|
|
3735
|
+
}
|
|
3736
|
+
function contextOccupancy(input) {
|
|
3737
|
+
const percent = (0, import_tool_kit.getNumber)(input, ["context_usage_percent", "contextUsagePercent"]);
|
|
3738
|
+
if (percent === void 0 || percent < 0) return {};
|
|
3739
|
+
return { contextWindowPeakPct: percent / 100 };
|
|
3740
|
+
}
|
|
3741
|
+
function mapStop(turnDurationMs) {
|
|
3742
|
+
const durationBucket = (0, import_tool_kit.bucketDurationMs)(turnDurationMs);
|
|
3743
|
+
if (durationBucket === "30-60m" || durationBucket === "60m+") {
|
|
3744
|
+
return [{ eventType: "agent_loop_long", severity: durationBucket === "60m+" ? "high" : "medium", metadata: withHost({ durationBucket, reason: "long_session", trigger: "inferred" }) }];
|
|
3745
|
+
}
|
|
3746
|
+
return [];
|
|
3747
|
+
}
|
|
3748
|
+
function withParsedToolOutput(input) {
|
|
3749
|
+
const raw = input.tool_output;
|
|
3750
|
+
if (typeof raw !== "string") return input;
|
|
3751
|
+
try {
|
|
3752
|
+
const parsed = JSON.parse(raw);
|
|
3753
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return input;
|
|
3754
|
+
return { ...input, result: parsed };
|
|
3755
|
+
} catch {
|
|
3756
|
+
return input;
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
function getCommand(input) {
|
|
3760
|
+
return (0, import_tool_kit.getString)(input, ["command"]) ?? (0, import_tool_kit.getNestedString)(input, [["tool_input", "command"]]);
|
|
3761
|
+
}
|
|
3762
|
+
function getToolName(input) {
|
|
3763
|
+
return (0, import_tool_kit.getString)(input, ["tool_name", "toolName"]);
|
|
3764
|
+
}
|
|
3765
|
+
function isShell(toolName) {
|
|
3766
|
+
const value = toolName?.toLowerCase();
|
|
3767
|
+
return value === "shell" || value === "bash" || value === "terminal";
|
|
3768
|
+
}
|
|
3769
|
+
function isWriteTool(toolName) {
|
|
3770
|
+
const value = toolName?.toLowerCase();
|
|
3771
|
+
return value === "write" || value === "edit" || value === "multiedit" || value === "search_replace" || value === "apply_patch";
|
|
3772
|
+
}
|
|
3773
|
+
function sanitiseToolName(toolName) {
|
|
3774
|
+
if (!toolName) return "unknown";
|
|
3775
|
+
return toolName.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 40) || "unknown";
|
|
3776
|
+
}
|
|
3777
|
+
function withHost(metadata) {
|
|
3778
|
+
return { host: CURSOR_HOST, ...metadata };
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3781
|
+
// src/setup.ts
|
|
3782
|
+
import * as os from "os";
|
|
3783
|
+
import * as path from "path";
|
|
3784
|
+
var HOOK_EVENTS = ["sessionStart", "sessionEnd", "beforeSubmitPrompt", "preToolUse", "postToolUse", "postToolUseFailure", "preCompact", "stop"];
|
|
3785
|
+
var SETUP = {
|
|
3786
|
+
host: CURSOR_HOST,
|
|
3787
|
+
displayName: "Cursor",
|
|
3788
|
+
toolType: ASCENDA_TOOL_TYPE,
|
|
3789
|
+
packageName: "@ascenda-one/cursor-hooks",
|
|
3790
|
+
binaryName: "ascenda-cursor-hook",
|
|
3791
|
+
hookEvents: HOOK_EVENTS,
|
|
3792
|
+
restartHint: "Restart Cursor to load the hooks.",
|
|
3793
|
+
settings: {
|
|
3794
|
+
settingsPath: (scope, projectDir) => scope === "user" ? path.join(os.homedir(), ".cursor", "hooks.json") : path.join(projectDir, ".cursor", "hooks.json"),
|
|
3795
|
+
// Cursor's hooks.json is versioned; a file we create must say which.
|
|
3796
|
+
scaffold: { version: 1 },
|
|
3797
|
+
// Cursor passes the event on stdin too, but this adapter reads argv, so
|
|
3798
|
+
// each entry names its event.
|
|
3799
|
+
entry: (command, event) => ({ command: `${command} ${event}` }),
|
|
3800
|
+
commandOf: (entry) => entry && typeof entry === "object" ? entry.command : void 0
|
|
3801
|
+
}
|
|
3802
|
+
};
|
|
3803
|
+
|
|
3804
|
+
// src/cli.ts
|
|
3805
|
+
async function main() {
|
|
3806
|
+
const command = process.argv[2];
|
|
3807
|
+
if ((0, import_tool_kit2.isCliAgentManagementCommand)(command)) {
|
|
3808
|
+
managementExitCode = await (0, import_tool_kit2.runCliAgentSetup)(process.argv.slice(2), SETUP);
|
|
3809
|
+
return;
|
|
3810
|
+
}
|
|
3811
|
+
const hookName = command;
|
|
3812
|
+
if (!hookName) {
|
|
3813
|
+
console.error("Usage: ascenda-cursor-hook <cursorHookEventName> | setup | status | uninstall");
|
|
3814
|
+
return;
|
|
3815
|
+
}
|
|
3816
|
+
const input = await readJsonFromStdin();
|
|
3817
|
+
const sessionId = typeof input.conversation_id === "string" ? input.conversation_id : typeof input.session_id === "string" ? input.session_id : void 0;
|
|
3818
|
+
let turnDurationMs;
|
|
3819
|
+
if (hookName === "beforeSubmitPrompt") (0, import_tool_kit2.recordTurnStart)(CURSOR_HOST, sessionId);
|
|
3820
|
+
if (hookName === "stop") turnDurationMs = (0, import_tool_kit2.consumeTurnDurationMs)(CURSOR_HOST, sessionId);
|
|
3821
|
+
await (0, import_tool_kit2.deliverHookEvents)(mapCursorEvent(hookName, input, turnDurationMs), {
|
|
3822
|
+
toolType: ASCENDA_TOOL_TYPE,
|
|
3823
|
+
host: CURSOR_HOST,
|
|
3824
|
+
setupCommand: `npx ${SETUP.packageName} setup`,
|
|
3825
|
+
source: "cli_agent",
|
|
3826
|
+
sessionId
|
|
3827
|
+
});
|
|
3828
|
+
}
|
|
3829
|
+
var managementExitCode;
|
|
3830
|
+
async function readJsonFromStdin() {
|
|
3831
|
+
const chunks = [];
|
|
3832
|
+
for await (const chunk of process.stdin) {
|
|
3833
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
3834
|
+
}
|
|
3835
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
3836
|
+
if (!raw) return {};
|
|
3837
|
+
try {
|
|
3838
|
+
const parsed = JSON.parse(raw);
|
|
3839
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
3840
|
+
return parsed;
|
|
3841
|
+
} catch {
|
|
3842
|
+
return {};
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
main().catch((error) => {
|
|
3846
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
3847
|
+
}).finally(() => process.exit(managementExitCode ?? 0));
|