@hasna/instructions 0.4.3 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +40 -250
- package/dist/mcp/index.js +1 -1
- package/dist/server/index.js +37 -57
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -4486,7 +4486,7 @@ var init_package_manager_guard = __esm(() => {
|
|
|
4486
4486
|
];
|
|
4487
4487
|
});
|
|
4488
4488
|
|
|
4489
|
-
// node_modules
|
|
4489
|
+
// node_modules/@hasna/events/dist/commander.js
|
|
4490
4490
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
4491
4491
|
import { existsSync } from "fs";
|
|
4492
4492
|
import { homedir } from "os";
|
|
@@ -4503,83 +4503,29 @@ function getPathValue(input, path) {
|
|
|
4503
4503
|
return;
|
|
4504
4504
|
}, input);
|
|
4505
4505
|
}
|
|
4506
|
-
function
|
|
4507
|
-
const
|
|
4508
|
-
|
|
4509
|
-
if (!values.some((item) => Object.is(item, value)))
|
|
4510
|
-
values.push(value);
|
|
4511
|
-
};
|
|
4512
|
-
if (path.includes(".") && path in input)
|
|
4513
|
-
push(input[path]);
|
|
4514
|
-
const nestedValue = getPathValue(input, path);
|
|
4515
|
-
if (nestedValue !== undefined || !path.includes("."))
|
|
4516
|
-
push(nestedValue);
|
|
4517
|
-
return values;
|
|
4518
|
-
}
|
|
4519
|
-
function wildcardToRegExp(pattern, options = {}) {
|
|
4520
|
-
let body = "";
|
|
4521
|
-
for (let index = 0;index < pattern.length; index += 1) {
|
|
4522
|
-
const char = pattern[index];
|
|
4523
|
-
if (char === "*") {
|
|
4524
|
-
if (pattern[index + 1] === "*") {
|
|
4525
|
-
body += ".*";
|
|
4526
|
-
index += 1;
|
|
4527
|
-
} else {
|
|
4528
|
-
body += options.segmentSafe ? "[^/]*" : ".*";
|
|
4529
|
-
}
|
|
4530
|
-
} else {
|
|
4531
|
-
body += char.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
4532
|
-
}
|
|
4533
|
-
}
|
|
4534
|
-
return new RegExp(`^${body}$`);
|
|
4506
|
+
function wildcardToRegExp(pattern) {
|
|
4507
|
+
const escaped = pattern.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/\*/g, ".*");
|
|
4508
|
+
return new RegExp(`^${escaped}$`);
|
|
4535
4509
|
}
|
|
4536
|
-
function matchString(value, matcher
|
|
4510
|
+
function matchString(value, matcher) {
|
|
4537
4511
|
if (matcher === undefined)
|
|
4538
4512
|
return true;
|
|
4539
4513
|
if (value === undefined)
|
|
4540
4514
|
return false;
|
|
4541
4515
|
const matchers = Array.isArray(matcher) ? matcher : [matcher];
|
|
4542
|
-
return matchers.some((item) => wildcardToRegExp(item
|
|
4516
|
+
return matchers.some((item) => wildcardToRegExp(item).test(value));
|
|
4543
4517
|
}
|
|
4544
4518
|
function matchRecord(input, matcher) {
|
|
4545
4519
|
if (!matcher)
|
|
4546
4520
|
return true;
|
|
4547
4521
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
4548
|
-
const
|
|
4549
|
-
|
|
4522
|
+
const actual = getPathValue(input, path);
|
|
4523
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
4524
|
+
return matchString(actual === undefined ? undefined : String(actual), expected);
|
|
4525
|
+
}
|
|
4526
|
+
return actual === expected;
|
|
4550
4527
|
});
|
|
4551
4528
|
}
|
|
4552
|
-
function matchField(actualValues, expected, path) {
|
|
4553
|
-
if (isNegativeMatcher(expected)) {
|
|
4554
|
-
return !actualValues.some((actual) => matchPositiveField(actual, expected.not, path));
|
|
4555
|
-
}
|
|
4556
|
-
return actualValues.some((actual) => matchPositiveField(actual, expected, path));
|
|
4557
|
-
}
|
|
4558
|
-
function matchPositiveField(actual, expected, path) {
|
|
4559
|
-
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
4560
|
-
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
4561
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
4562
|
-
}));
|
|
4563
|
-
}
|
|
4564
|
-
if (Array.isArray(actual)) {
|
|
4565
|
-
return actual.some((item) => item === expected);
|
|
4566
|
-
}
|
|
4567
|
-
return actual === expected;
|
|
4568
|
-
}
|
|
4569
|
-
function stringCandidates(actual) {
|
|
4570
|
-
if (actual === undefined)
|
|
4571
|
-
return [];
|
|
4572
|
-
if (Array.isArray(actual)) {
|
|
4573
|
-
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
4574
|
-
}
|
|
4575
|
-
return [String(actual)];
|
|
4576
|
-
}
|
|
4577
|
-
function isPrimitiveFieldValue(value) {
|
|
4578
|
-
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
4579
|
-
}
|
|
4580
|
-
function isNegativeMatcher(value) {
|
|
4581
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
4582
|
-
}
|
|
4583
4529
|
function eventMatchesFilter(event, filter) {
|
|
4584
4530
|
return matchString(event.source, filter.source) && matchString(event.type, filter.type) && matchString(event.subject, filter.subject) && matchString(event.severity, filter.severity) && matchRecord(event.data, filter.data) && matchRecord(event.metadata, filter.metadata);
|
|
4585
4531
|
}
|
|
@@ -4595,14 +4541,6 @@ var HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME";
|
|
|
4595
4541
|
function getEventsDataDir(override) {
|
|
4596
4542
|
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join(homedir(), ".hasna", "events");
|
|
4597
4543
|
}
|
|
4598
|
-
function getActiveEventsDirEnv() {
|
|
4599
|
-
if (process.env[HASNA_EVENTS_DIR_ENV])
|
|
4600
|
-
return HASNA_EVENTS_DIR_ENV;
|
|
4601
|
-
if (process.env[HASNA_EVENTS_HOME_ENV])
|
|
4602
|
-
return HASNA_EVENTS_HOME_ENV;
|
|
4603
|
-
return null;
|
|
4604
|
-
}
|
|
4605
|
-
|
|
4606
4544
|
class JsonEventsStore {
|
|
4607
4545
|
dataDir;
|
|
4608
4546
|
channelsPath;
|
|
@@ -4714,52 +4652,6 @@ class JsonEventsStore {
|
|
|
4714
4652
|
});
|
|
4715
4653
|
}
|
|
4716
4654
|
}
|
|
4717
|
-
async function getEventsStatus(dataDir) {
|
|
4718
|
-
const store = new JsonEventsStore(dataDir);
|
|
4719
|
-
await store.init();
|
|
4720
|
-
const [channels, events, deliveries] = await Promise.all([
|
|
4721
|
-
store.listChannels(),
|
|
4722
|
-
store.listEvents(),
|
|
4723
|
-
store.listDeliveries()
|
|
4724
|
-
]);
|
|
4725
|
-
const transports = channels.reduce((counts, channel) => {
|
|
4726
|
-
counts[channel.transport] = (counts[channel.transport] ?? 0) + 1;
|
|
4727
|
-
return counts;
|
|
4728
|
-
}, {});
|
|
4729
|
-
return {
|
|
4730
|
-
service: "events",
|
|
4731
|
-
schemaVersion: "1.0",
|
|
4732
|
-
dataDir: store.dataDir,
|
|
4733
|
-
env: {
|
|
4734
|
-
primary: HASNA_EVENTS_DIR_ENV,
|
|
4735
|
-
fallback: HASNA_EVENTS_HOME_ENV,
|
|
4736
|
-
active: getActiveEventsDirEnv()
|
|
4737
|
-
},
|
|
4738
|
-
files: {
|
|
4739
|
-
channels: statusFile(store.dataDir, "channels.json", channels.length),
|
|
4740
|
-
events: statusFile(store.dataDir, "events.json", events.length),
|
|
4741
|
-
deliveries: statusFile(store.dataDir, "deliveries.json", deliveries.length)
|
|
4742
|
-
},
|
|
4743
|
-
counts: {
|
|
4744
|
-
channels: channels.length,
|
|
4745
|
-
enabledChannels: channels.filter((channel) => channel.enabled).length,
|
|
4746
|
-
disabledChannels: channels.filter((channel) => !channel.enabled).length,
|
|
4747
|
-
events: events.length,
|
|
4748
|
-
deliveries: deliveries.length
|
|
4749
|
-
},
|
|
4750
|
-
transports,
|
|
4751
|
-
safety: {
|
|
4752
|
-
includesEventPayloads: false,
|
|
4753
|
-
includesWebhookSecrets: false,
|
|
4754
|
-
listOutputsRedactSecrets: true,
|
|
4755
|
-
statusOutputIsMetadataOnly: true
|
|
4756
|
-
}
|
|
4757
|
-
};
|
|
4758
|
-
}
|
|
4759
|
-
function statusFile(dataDir, fileName, records) {
|
|
4760
|
-
const path = join(dataDir, fileName);
|
|
4761
|
-
return { path, exists: existsSync(path), records };
|
|
4762
|
-
}
|
|
4763
4655
|
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
4764
4656
|
function buildSignatureBase(timestamp, body) {
|
|
4765
4657
|
return `${timestamp}.${body}`;
|
|
@@ -4985,7 +4877,7 @@ class EventsClient {
|
|
|
4985
4877
|
}
|
|
4986
4878
|
return deliveries;
|
|
4987
4879
|
}
|
|
4988
|
-
async
|
|
4880
|
+
async testChannel(id, input = {}) {
|
|
4989
4881
|
const channel = await this.store.getChannel(id);
|
|
4990
4882
|
if (!channel)
|
|
4991
4883
|
throw new Error(`Channel not found: ${id}`);
|
|
@@ -5002,34 +4894,6 @@ class EventsClient {
|
|
|
5002
4894
|
time: input.time,
|
|
5003
4895
|
id: input.id
|
|
5004
4896
|
});
|
|
5005
|
-
const matched = channelMatchesEvent(channel, event);
|
|
5006
|
-
return {
|
|
5007
|
-
channelId: channel.id,
|
|
5008
|
-
matched,
|
|
5009
|
-
event,
|
|
5010
|
-
filters: channel.filters,
|
|
5011
|
-
reason: matched ? undefined : channel.enabled ? "event did not match channel filters" : "channel is disabled"
|
|
5012
|
-
};
|
|
5013
|
-
}
|
|
5014
|
-
async testChannel(id, input = {}, options = {}) {
|
|
5015
|
-
const channel = await this.store.getChannel(id);
|
|
5016
|
-
if (!channel)
|
|
5017
|
-
throw new Error(`Channel not found: ${id}`);
|
|
5018
|
-
const match = await this.matchChannel(id, input);
|
|
5019
|
-
const event = match.event;
|
|
5020
|
-
if (options.honorFilters && !match.matched) {
|
|
5021
|
-
const timestamp = new Date().toISOString();
|
|
5022
|
-
const result2 = createDeliveryResult(event, channel, [{
|
|
5023
|
-
attempt: 1,
|
|
5024
|
-
status: "skipped",
|
|
5025
|
-
startedAt: timestamp,
|
|
5026
|
-
completedAt: timestamp,
|
|
5027
|
-
error: match.reason
|
|
5028
|
-
}]);
|
|
5029
|
-
result2.metadata = { reason: "filter_mismatch" };
|
|
5030
|
-
await this.store.appendDelivery(result2);
|
|
5031
|
-
return result2;
|
|
5032
|
-
}
|
|
5033
4897
|
const eventForChannel = await this.applyRedaction(event, channel);
|
|
5034
4898
|
const result = await this.deliverWithRetry(eventForChannel, channel);
|
|
5035
4899
|
await this.store.appendDelivery(result);
|
|
@@ -5140,76 +5004,6 @@ function normalizeRetryPolicy(policy) {
|
|
|
5140
5004
|
multiplier: Math.max(1, policy?.multiplier ?? 2)
|
|
5141
5005
|
};
|
|
5142
5006
|
}
|
|
5143
|
-
function parseFieldMatchers(values, label, typed = false) {
|
|
5144
|
-
if (!values?.length)
|
|
5145
|
-
return;
|
|
5146
|
-
const result = {};
|
|
5147
|
-
for (const value of values) {
|
|
5148
|
-
const parsed = parseMatcherExpression(value, label);
|
|
5149
|
-
const path = parsed.path;
|
|
5150
|
-
if (path in result)
|
|
5151
|
-
throw new Error(`Duplicate ${label} filter path: ${path}`);
|
|
5152
|
-
const matcherValue = typed ? parseTypedMatcherValue(parsed.rawValue, label) : parsed.rawValue;
|
|
5153
|
-
result[path] = parsed.negated ? { not: matcherValue } : matcherValue;
|
|
5154
|
-
}
|
|
5155
|
-
return result;
|
|
5156
|
-
}
|
|
5157
|
-
function parseFilterOptions(options) {
|
|
5158
|
-
const filter2 = {};
|
|
5159
|
-
if (options.source)
|
|
5160
|
-
filter2.source = options.source;
|
|
5161
|
-
if (options.type)
|
|
5162
|
-
filter2.type = options.type;
|
|
5163
|
-
if (options.subject)
|
|
5164
|
-
filter2.subject = options.subject;
|
|
5165
|
-
if (options.severity)
|
|
5166
|
-
filter2.severity = options.severity;
|
|
5167
|
-
const data = mergeMatchers(parseFieldMatchers(options.data, "data"), parseFieldMatchers(options.dataJson, "data-json", true));
|
|
5168
|
-
const metadata = mergeMatchers(parseFieldMatchers(options.metadata, "metadata"), parseFieldMatchers(options.metadataJson, "metadata-json", true));
|
|
5169
|
-
if (Object.keys(data).length > 0)
|
|
5170
|
-
filter2.data = data;
|
|
5171
|
-
if (Object.keys(metadata).length > 0)
|
|
5172
|
-
filter2.metadata = metadata;
|
|
5173
|
-
return Object.keys(filter2).length > 0 ? [filter2] : undefined;
|
|
5174
|
-
}
|
|
5175
|
-
function mergeMatchers(...records) {
|
|
5176
|
-
const result = {};
|
|
5177
|
-
for (const record of records) {
|
|
5178
|
-
if (!record)
|
|
5179
|
-
continue;
|
|
5180
|
-
for (const [path, value] of Object.entries(record)) {
|
|
5181
|
-
if (path in result)
|
|
5182
|
-
throw new Error(`Duplicate filter path: ${path}`);
|
|
5183
|
-
result[path] = value;
|
|
5184
|
-
}
|
|
5185
|
-
}
|
|
5186
|
-
return result;
|
|
5187
|
-
}
|
|
5188
|
-
function parseTypedMatcherValue(value, label) {
|
|
5189
|
-
const parsed = JSON.parse(value);
|
|
5190
|
-
if (parsed === null || typeof parsed === "string" || typeof parsed === "number" || typeof parsed === "boolean" || Array.isArray(parsed) && parsed.every((item) => typeof item === "string")) {
|
|
5191
|
-
return parsed;
|
|
5192
|
-
}
|
|
5193
|
-
throw new Error(`${label} filter JSON values must be string, string[], number, boolean, or null`);
|
|
5194
|
-
}
|
|
5195
|
-
function parseMatcherExpression(value, label) {
|
|
5196
|
-
const negativeSeparator = value.indexOf("!=");
|
|
5197
|
-
if (negativeSeparator > 0) {
|
|
5198
|
-
return {
|
|
5199
|
-
path: value.slice(0, negativeSeparator),
|
|
5200
|
-
rawValue: value.slice(negativeSeparator + 2),
|
|
5201
|
-
negated: true
|
|
5202
|
-
};
|
|
5203
|
-
}
|
|
5204
|
-
const separator = value.indexOf("=");
|
|
5205
|
-
if (separator <= 0)
|
|
5206
|
-
throw new Error(`Invalid ${label} filter, expected path=value or path!=value: ${value}`);
|
|
5207
|
-
return {
|
|
5208
|
-
path: value.slice(0, separator),
|
|
5209
|
-
rawValue: value.slice(separator + 1),
|
|
5210
|
-
negated: false
|
|
5211
|
-
};
|
|
5212
|
-
}
|
|
5213
5007
|
function parseJsonObject(value, fallback) {
|
|
5214
5008
|
if (!value)
|
|
5215
5009
|
return fallback;
|
|
@@ -5231,6 +5025,18 @@ function parseHeaders(values) {
|
|
|
5231
5025
|
}
|
|
5232
5026
|
return headers;
|
|
5233
5027
|
}
|
|
5028
|
+
function parseFilter(options) {
|
|
5029
|
+
const filter2 = {};
|
|
5030
|
+
if (options.source)
|
|
5031
|
+
filter2.source = options.source;
|
|
5032
|
+
if (options.type)
|
|
5033
|
+
filter2.type = options.type;
|
|
5034
|
+
if (options.subject)
|
|
5035
|
+
filter2.subject = options.subject;
|
|
5036
|
+
if (options.severity)
|
|
5037
|
+
filter2.severity = options.severity;
|
|
5038
|
+
return Object.keys(filter2).length > 0 ? [filter2] : undefined;
|
|
5039
|
+
}
|
|
5234
5040
|
function createClient(options) {
|
|
5235
5041
|
if (options.createClient)
|
|
5236
5042
|
return options.createClient();
|
|
@@ -5248,16 +5054,16 @@ function hasJsonOption(options) {
|
|
|
5248
5054
|
function wantsJson(actionOptions, command) {
|
|
5249
5055
|
return hasJsonOption(actionOptions) || hasJsonOption(command);
|
|
5250
5056
|
}
|
|
5251
|
-
function
|
|
5252
|
-
const
|
|
5253
|
-
|
|
5057
|
+
function registerWebhookCommands(program, options) {
|
|
5058
|
+
const webhooks = program.command(options.webhooksCommandName ?? "webhooks").description("Manage Hasna event webhook subscriptions");
|
|
5059
|
+
webhooks.command("add").description("Add or replace a webhook or command subscription").argument("<target>", "Webhook URL or command binary").requiredOption("--id <id>", "Subscription/channel identifier").option("--transport <kind>", "Transport kind: webhook or command", "webhook").option("--name <name>", "Display name").option("--type <pattern>", "Event type filter, e.g. todos.task.*").option("--source <pattern>", "Event source filter").option("--subject <pattern>", "Event subject filter").option("--severity <pattern>", "Event severity filter").option("--secret <secret>", "Webhook HMAC secret").option("--header <name=value...>", "Webhook header", collectValues, []).option("--arg <arg...>", "Command argument", collectValues, []).option("--timeout-ms <ms>", "Transport timeout in milliseconds", parseNumber).option("--retry-attempts <n>", "Maximum delivery attempts", parseNumber).option("--retry-backoff-ms <ms>", "Initial retry backoff in milliseconds", parseNumber).option("--redact <path...>", "Event field path to redact before delivery", collectValues, []).option("--disabled", "Create channel disabled", false).option("-j, --json", "Print JSON output", false).action(async (target, actionOptions, command) => {
|
|
5254
5060
|
const timestamp = new Date().toISOString();
|
|
5255
5061
|
const channel = {
|
|
5256
5062
|
id: actionOptions.id,
|
|
5257
5063
|
name: actionOptions.name,
|
|
5258
5064
|
enabled: !actionOptions.disabled,
|
|
5259
5065
|
transport: actionOptions.transport,
|
|
5260
|
-
filters:
|
|
5066
|
+
filters: parseFilter(actionOptions),
|
|
5261
5067
|
retry: actionOptions.retryAttempts || actionOptions.retryBackoffMs ? { maxAttempts: actionOptions.retryAttempts, backoffMs: actionOptions.retryBackoffMs } : undefined,
|
|
5262
5068
|
redact: actionOptions.redact?.length ? { paths: actionOptions.redact } : undefined,
|
|
5263
5069
|
createdAt: timestamp,
|
|
@@ -5273,51 +5079,35 @@ function registerChannelCommands(program, options) {
|
|
|
5273
5079
|
const saved = await createClient(options).addChannel(channel);
|
|
5274
5080
|
print(sanitizeChannelForOutput(saved), wantsJson(actionOptions, command), `Added ${saved.transport} channel ${saved.id}`);
|
|
5275
5081
|
});
|
|
5276
|
-
|
|
5277
|
-
const
|
|
5082
|
+
webhooks.command("list").description("List configured subscriptions").option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
|
|
5083
|
+
const channels = await createClient(options).listChannels();
|
|
5278
5084
|
if (wantsJson(actionOptions, command)) {
|
|
5279
|
-
console.log(JSON.stringify(sanitizeChannelsForOutput(
|
|
5085
|
+
console.log(JSON.stringify(sanitizeChannelsForOutput(channels), null, 2));
|
|
5280
5086
|
return;
|
|
5281
5087
|
}
|
|
5282
|
-
if (!
|
|
5088
|
+
if (!channels.length) {
|
|
5283
5089
|
console.log("No channels configured.");
|
|
5284
5090
|
return;
|
|
5285
5091
|
}
|
|
5286
|
-
for (const channel of
|
|
5092
|
+
for (const channel of channels) {
|
|
5287
5093
|
console.log(`${channel.id} ${channel.enabled ? "enabled" : "disabled"} ${channel.transport} ${channel.webhook?.url ?? channel.command?.command ?? channel.transport}`);
|
|
5288
5094
|
}
|
|
5289
5095
|
});
|
|
5290
|
-
|
|
5291
|
-
const status = await getEventsStatus(options.dataDir);
|
|
5292
|
-
print(status, wantsJson(actionOptions, command), `events dataDir: ${status.dataDir}`);
|
|
5293
|
-
});
|
|
5294
|
-
channels.command("remove").description("Remove a channel").argument("<id>", "Channel identifier").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
|
|
5096
|
+
webhooks.command("remove").description("Remove a subscription").argument("<id>", "Subscription/channel identifier").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
|
|
5295
5097
|
const removed = await createClient(options).removeChannel(id);
|
|
5296
5098
|
print({ removed }, wantsJson(actionOptions, command), removed ? `Removed ${id}` : `Channel not found: ${id}`);
|
|
5297
5099
|
});
|
|
5298
|
-
|
|
5100
|
+
webhooks.command("test").description("Send a test event to one subscription").argument("<id>", "Subscription/channel identifier").option("--type <type>", "Event type", "events.test").option("--subject <subject>", "Event subject").option("--message <message>", "Event message", "Hasna events test delivery").option("--data <json>", "Event data JSON object").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
|
|
5299
5101
|
const result = await createClient(options).testChannel(id, {
|
|
5300
|
-
source:
|
|
5301
|
-
type: actionOptions.type,
|
|
5302
|
-
subject: actionOptions.subject ?? id,
|
|
5303
|
-
message: actionOptions.message,
|
|
5304
|
-
data: parseJsonObject(actionOptions.data, { test: true }),
|
|
5305
|
-
metadata: parseJsonObject(actionOptions.metadata, {})
|
|
5306
|
-
}, { honorFilters: actionOptions.honorFilters });
|
|
5307
|
-
print(result, wantsJson(actionOptions, command), `${result.status}: ${result.channelId}`);
|
|
5308
|
-
});
|
|
5309
|
-
channels.command("match").description("Check whether a sample event matches one channel without delivering").argument("<id>", "Channel identifier").option("--source <source>", "Event source override").option("--type <type>", "Event type", "events.test").option("--subject <subject>", "Event subject").option("--message <message>", "Event message", "Hasna events match preview").option("--data <json>", "Event data JSON object").option("--metadata <json>", "Event metadata JSON object").option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
|
|
5310
|
-
const result = await createClient(options).matchChannel(id, {
|
|
5311
|
-
source: actionOptions.source ?? options.source,
|
|
5102
|
+
source: options.source,
|
|
5312
5103
|
type: actionOptions.type,
|
|
5313
5104
|
subject: actionOptions.subject ?? id,
|
|
5314
5105
|
message: actionOptions.message,
|
|
5315
|
-
data: parseJsonObject(actionOptions.data, { test: true })
|
|
5316
|
-
metadata: parseJsonObject(actionOptions.metadata, {})
|
|
5106
|
+
data: parseJsonObject(actionOptions.data, { test: true })
|
|
5317
5107
|
});
|
|
5318
|
-
print(result, wantsJson(actionOptions, command), `${result.
|
|
5108
|
+
print(result, wantsJson(actionOptions, command), `${result.status}: ${result.channelId}`);
|
|
5319
5109
|
});
|
|
5320
|
-
return
|
|
5110
|
+
return webhooks;
|
|
5321
5111
|
}
|
|
5322
5112
|
function registerEventCommands(program, options) {
|
|
5323
5113
|
const events = program.command(options.eventsCommandName ?? "events").description("Emit, list, and replay Hasna events");
|
|
@@ -5365,7 +5155,7 @@ function registerEventCommands(program, options) {
|
|
|
5365
5155
|
return events;
|
|
5366
5156
|
}
|
|
5367
5157
|
function registerEventsCommands(program, options) {
|
|
5368
|
-
|
|
5158
|
+
registerWebhookCommands(program, options);
|
|
5369
5159
|
registerEventCommands(program, options);
|
|
5370
5160
|
}
|
|
5371
5161
|
function parseNumber(value) {
|
package/dist/mcp/index.js
CHANGED
|
@@ -2040,7 +2040,7 @@ var init_sync_dir = __esm(() => {
|
|
|
2040
2040
|
var require_package = __commonJS((exports, module) => {
|
|
2041
2041
|
module.exports = {
|
|
2042
2042
|
name: "@hasna/instructions",
|
|
2043
|
-
version: "0.4.
|
|
2043
|
+
version: "0.4.4",
|
|
2044
2044
|
description: "AI coding agent instruction & configuration manager \u2014 store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
|
|
2045
2045
|
type: "module",
|
|
2046
2046
|
main: "dist/index.js",
|
package/dist/server/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
var __require = import.meta.require;
|
|
4
4
|
|
|
5
|
-
// node_modules
|
|
5
|
+
// node_modules/hono/dist/compose.js
|
|
6
6
|
var compose = (middleware, onError, onNotFound) => {
|
|
7
7
|
return (context, next) => {
|
|
8
8
|
let index = -1;
|
|
@@ -46,39 +46,21 @@ var compose = (middleware, onError, onNotFound) => {
|
|
|
46
46
|
};
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
// node_modules
|
|
49
|
+
// node_modules/hono/dist/request/constants.js
|
|
50
50
|
var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
|
|
51
51
|
|
|
52
|
-
// node_modules
|
|
53
|
-
var bufferToFormData = (arrayBuffer, contentType) => {
|
|
54
|
-
const response = new Response(arrayBuffer, {
|
|
55
|
-
headers: {
|
|
56
|
-
"Content-Type": contentType.replace(/^[^;]+/, (mediaType) => mediaType.toLowerCase())
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
return response.formData();
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/utils/body.js
|
|
63
|
-
var isRawRequest = (request) => ("headers" in request);
|
|
52
|
+
// node_modules/hono/dist/utils/body.js
|
|
64
53
|
var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
|
|
65
54
|
const { all = false, dot = false } = options;
|
|
66
|
-
const headers =
|
|
55
|
+
const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
|
|
67
56
|
const contentType = headers.get("Content-Type");
|
|
68
|
-
|
|
69
|
-
if (mediaType === "multipart/form-data" || mediaType === "application/x-www-form-urlencoded") {
|
|
57
|
+
if (contentType?.startsWith("multipart/form-data") || contentType?.startsWith("application/x-www-form-urlencoded")) {
|
|
70
58
|
return parseFormData(request, { all, dot });
|
|
71
59
|
}
|
|
72
60
|
return {};
|
|
73
61
|
};
|
|
74
62
|
async function parseFormData(request, options) {
|
|
75
|
-
const
|
|
76
|
-
const arrayBuffer = await request.arrayBuffer();
|
|
77
|
-
const formDataPromise = bufferToFormData(arrayBuffer, headers.get("Content-Type") || "");
|
|
78
|
-
if (!isRawRequest(request)) {
|
|
79
|
-
request.bodyCache.formData = formDataPromise;
|
|
80
|
-
}
|
|
81
|
-
const formData = await formDataPromise;
|
|
63
|
+
const formData = await request.formData();
|
|
82
64
|
if (formData) {
|
|
83
65
|
return convertFormDataToBodyData(formData, options);
|
|
84
66
|
}
|
|
@@ -138,7 +120,7 @@ var handleParsingNestedValues = (form, key, value) => {
|
|
|
138
120
|
});
|
|
139
121
|
};
|
|
140
122
|
|
|
141
|
-
// node_modules
|
|
123
|
+
// node_modules/hono/dist/utils/url.js
|
|
142
124
|
var splitPath = (path) => {
|
|
143
125
|
const paths = path.split("/");
|
|
144
126
|
if (paths[0] === "") {
|
|
@@ -338,7 +320,7 @@ var getQueryParams = (url, key) => {
|
|
|
338
320
|
};
|
|
339
321
|
var decodeURIComponent_ = decodeURIComponent;
|
|
340
322
|
|
|
341
|
-
// node_modules
|
|
323
|
+
// node_modules/hono/dist/request.js
|
|
342
324
|
var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
|
|
343
325
|
var HonoRequest = class {
|
|
344
326
|
raw;
|
|
@@ -420,9 +402,6 @@ var HonoRequest = class {
|
|
|
420
402
|
arrayBuffer() {
|
|
421
403
|
return this.#cachedBody("arrayBuffer");
|
|
422
404
|
}
|
|
423
|
-
bytes() {
|
|
424
|
-
return this.#cachedBody("arrayBuffer").then((buffer) => new Uint8Array(buffer));
|
|
425
|
-
}
|
|
426
405
|
blob() {
|
|
427
406
|
return this.#cachedBody("blob");
|
|
428
407
|
}
|
|
@@ -452,7 +431,7 @@ var HonoRequest = class {
|
|
|
452
431
|
}
|
|
453
432
|
};
|
|
454
433
|
|
|
455
|
-
// node_modules
|
|
434
|
+
// node_modules/hono/dist/utils/html.js
|
|
456
435
|
var HtmlEscapedCallbackPhase = {
|
|
457
436
|
Stringify: 1,
|
|
458
437
|
BeforeStream: 2,
|
|
@@ -490,7 +469,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
|
|
|
490
469
|
}
|
|
491
470
|
};
|
|
492
471
|
|
|
493
|
-
// node_modules
|
|
472
|
+
// node_modules/hono/dist/context.js
|
|
494
473
|
var TEXT_PLAIN = "text/plain; charset=UTF-8";
|
|
495
474
|
var setDefaultContentType = (contentType, headers) => {
|
|
496
475
|
return {
|
|
@@ -657,7 +636,7 @@ var Context = class {
|
|
|
657
636
|
};
|
|
658
637
|
};
|
|
659
638
|
|
|
660
|
-
// node_modules
|
|
639
|
+
// node_modules/hono/dist/router.js
|
|
661
640
|
var METHOD_NAME_ALL = "ALL";
|
|
662
641
|
var METHOD_NAME_ALL_LOWERCASE = "all";
|
|
663
642
|
var METHODS = ["get", "post", "put", "delete", "options", "patch"];
|
|
@@ -665,10 +644,10 @@ var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is
|
|
|
665
644
|
var UnsupportedPathError = class extends Error {
|
|
666
645
|
};
|
|
667
646
|
|
|
668
|
-
// node_modules
|
|
647
|
+
// node_modules/hono/dist/utils/constants.js
|
|
669
648
|
var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
|
|
670
649
|
|
|
671
|
-
// node_modules
|
|
650
|
+
// node_modules/hono/dist/hono-base.js
|
|
672
651
|
var notFoundHandler = (c) => {
|
|
673
652
|
return c.text("404 Not Found", 404);
|
|
674
653
|
};
|
|
@@ -759,7 +738,7 @@ var Hono = class _Hono {
|
|
|
759
738
|
handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;
|
|
760
739
|
handler[COMPOSED_HANDLER] = r.handler;
|
|
761
740
|
}
|
|
762
|
-
subApp.#addRoute(r.method, r.path, handler
|
|
741
|
+
subApp.#addRoute(r.method, r.path, handler);
|
|
763
742
|
});
|
|
764
743
|
return this;
|
|
765
744
|
}
|
|
@@ -806,7 +785,7 @@ var Hono = class _Hono {
|
|
|
806
785
|
const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
|
|
807
786
|
return (request) => {
|
|
808
787
|
const url = new URL(request.url);
|
|
809
|
-
url.pathname =
|
|
788
|
+
url.pathname = url.pathname.slice(pathPrefixLength) || "/";
|
|
810
789
|
return new Request(url, request);
|
|
811
790
|
};
|
|
812
791
|
})();
|
|
@@ -820,15 +799,10 @@ var Hono = class _Hono {
|
|
|
820
799
|
this.#addRoute(METHOD_NAME_ALL, mergePath(path, "*"), handler);
|
|
821
800
|
return this;
|
|
822
801
|
}
|
|
823
|
-
#addRoute(method, path, handler
|
|
802
|
+
#addRoute(method, path, handler) {
|
|
824
803
|
method = method.toUpperCase();
|
|
825
804
|
path = mergePath(this._basePath, path);
|
|
826
|
-
const r = {
|
|
827
|
-
basePath: baseRoutePath !== undefined ? mergePath(this._basePath, baseRoutePath) : this._basePath,
|
|
828
|
-
path,
|
|
829
|
-
method,
|
|
830
|
-
handler
|
|
831
|
-
};
|
|
805
|
+
const r = { basePath: this._basePath, path, method, handler };
|
|
832
806
|
this.router.add(method, path, [handler, r]);
|
|
833
807
|
this.routes.push(r);
|
|
834
808
|
}
|
|
@@ -892,7 +866,7 @@ var Hono = class _Hono {
|
|
|
892
866
|
};
|
|
893
867
|
};
|
|
894
868
|
|
|
895
|
-
// node_modules
|
|
869
|
+
// node_modules/hono/dist/router/reg-exp-router/matcher.js
|
|
896
870
|
var emptyParam = [];
|
|
897
871
|
function match(method, path) {
|
|
898
872
|
const matchers = this.buildAllMatchers();
|
|
@@ -913,7 +887,7 @@ function match(method, path) {
|
|
|
913
887
|
return match2(method, path);
|
|
914
888
|
}
|
|
915
889
|
|
|
916
|
-
// node_modules
|
|
890
|
+
// node_modules/hono/dist/router/reg-exp-router/node.js
|
|
917
891
|
var LABEL_REG_EXP_STR = "[^/]+";
|
|
918
892
|
var ONLY_WILDCARD_REG_EXP_STR = ".*";
|
|
919
893
|
var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
|
|
@@ -1017,7 +991,7 @@ var Node = class _Node {
|
|
|
1017
991
|
}
|
|
1018
992
|
};
|
|
1019
993
|
|
|
1020
|
-
// node_modules
|
|
994
|
+
// node_modules/hono/dist/router/reg-exp-router/trie.js
|
|
1021
995
|
var Trie = class {
|
|
1022
996
|
#context = { varIndex: 0 };
|
|
1023
997
|
#root = new Node;
|
|
@@ -1073,7 +1047,7 @@ var Trie = class {
|
|
|
1073
1047
|
}
|
|
1074
1048
|
};
|
|
1075
1049
|
|
|
1076
|
-
// node_modules
|
|
1050
|
+
// node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1077
1051
|
var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
|
|
1078
1052
|
var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
|
|
1079
1053
|
function buildWildcardRegExp(path) {
|
|
@@ -1238,7 +1212,7 @@ var RegExpRouter = class {
|
|
|
1238
1212
|
}
|
|
1239
1213
|
};
|
|
1240
1214
|
|
|
1241
|
-
// node_modules
|
|
1215
|
+
// node_modules/hono/dist/router/reg-exp-router/prepared-router.js
|
|
1242
1216
|
var PreparedRegExpRouter = class {
|
|
1243
1217
|
name = "PreparedRegExpRouter";
|
|
1244
1218
|
#matchers;
|
|
@@ -1310,7 +1284,7 @@ var PreparedRegExpRouter = class {
|
|
|
1310
1284
|
match = match;
|
|
1311
1285
|
};
|
|
1312
1286
|
|
|
1313
|
-
// node_modules
|
|
1287
|
+
// node_modules/hono/dist/router/smart-router/router.js
|
|
1314
1288
|
var SmartRouter = class {
|
|
1315
1289
|
name = "SmartRouter";
|
|
1316
1290
|
#routers = [];
|
|
@@ -1365,7 +1339,7 @@ var SmartRouter = class {
|
|
|
1365
1339
|
}
|
|
1366
1340
|
};
|
|
1367
1341
|
|
|
1368
|
-
// node_modules
|
|
1342
|
+
// node_modules/hono/dist/router/trie-router/node.js
|
|
1369
1343
|
var emptyParams = /* @__PURE__ */ Object.create(null);
|
|
1370
1344
|
var hasChildren = (children) => {
|
|
1371
1345
|
for (const _ in children) {
|
|
@@ -1534,7 +1508,7 @@ var Node2 = class _Node2 {
|
|
|
1534
1508
|
}
|
|
1535
1509
|
};
|
|
1536
1510
|
|
|
1537
|
-
// node_modules
|
|
1511
|
+
// node_modules/hono/dist/router/trie-router/router.js
|
|
1538
1512
|
var TrieRouter = class {
|
|
1539
1513
|
name = "TrieRouter";
|
|
1540
1514
|
#node;
|
|
@@ -1556,7 +1530,7 @@ var TrieRouter = class {
|
|
|
1556
1530
|
}
|
|
1557
1531
|
};
|
|
1558
1532
|
|
|
1559
|
-
// node_modules
|
|
1533
|
+
// node_modules/hono/dist/hono.js
|
|
1560
1534
|
var Hono2 = class extends Hono {
|
|
1561
1535
|
constructor(options = {}) {
|
|
1562
1536
|
super(options);
|
|
@@ -1566,18 +1540,24 @@ var Hono2 = class extends Hono {
|
|
|
1566
1540
|
}
|
|
1567
1541
|
};
|
|
1568
1542
|
|
|
1569
|
-
// node_modules
|
|
1543
|
+
// node_modules/hono/dist/middleware/cors/index.js
|
|
1570
1544
|
var cors = (options) => {
|
|
1571
|
-
const
|
|
1545
|
+
const defaults = {
|
|
1572
1546
|
origin: "*",
|
|
1573
1547
|
allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"],
|
|
1574
1548
|
allowHeaders: [],
|
|
1575
|
-
exposeHeaders: []
|
|
1549
|
+
exposeHeaders: []
|
|
1550
|
+
};
|
|
1551
|
+
const opts = {
|
|
1552
|
+
...defaults,
|
|
1576
1553
|
...options
|
|
1577
1554
|
};
|
|
1578
1555
|
const findAllowOrigin = ((optsOrigin) => {
|
|
1579
1556
|
if (typeof optsOrigin === "string") {
|
|
1580
1557
|
if (optsOrigin === "*") {
|
|
1558
|
+
if (opts.credentials) {
|
|
1559
|
+
return (origin) => origin || null;
|
|
1560
|
+
}
|
|
1581
1561
|
return () => optsOrigin;
|
|
1582
1562
|
} else {
|
|
1583
1563
|
return (origin) => optsOrigin === origin ? origin : null;
|
|
@@ -1612,7 +1592,7 @@ var cors = (options) => {
|
|
|
1612
1592
|
set("Access-Control-Expose-Headers", opts.exposeHeaders.join(","));
|
|
1613
1593
|
}
|
|
1614
1594
|
if (c.req.method === "OPTIONS") {
|
|
1615
|
-
if (opts.origin !== "*") {
|
|
1595
|
+
if (opts.origin !== "*" || opts.credentials) {
|
|
1616
1596
|
set("Vary", "Origin");
|
|
1617
1597
|
}
|
|
1618
1598
|
if (opts.maxAge != null) {
|
|
@@ -1642,7 +1622,7 @@ var cors = (options) => {
|
|
|
1642
1622
|
});
|
|
1643
1623
|
}
|
|
1644
1624
|
await next();
|
|
1645
|
-
if (opts.origin !== "*") {
|
|
1625
|
+
if (opts.origin !== "*" || opts.credentials) {
|
|
1646
1626
|
c.header("Vary", "Origin", { append: true });
|
|
1647
1627
|
}
|
|
1648
1628
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/instructions",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "AI coding agent instruction & configuration manager — store, version, apply, and share all your AI coding configs. CLI + MCP + HTTP API (instructions-serve) + generated SDK + Dashboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|