@hasna/instructions 0.4.1 → 0.4.3
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 +283 -98
- package/dist/db/database.d.ts +1 -1
- package/dist/db/database.d.ts.map +1 -1
- package/dist/index.js +13 -38
- package/dist/mcp/index.js +10 -32
- package/dist/server/index.js +59 -39
- package/dist/status.d.ts +2 -3
- package/dist/status.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2112,21 +2112,17 @@ var init_types = __esm(() => {
|
|
|
2112
2112
|
|
|
2113
2113
|
// src/db/database.ts
|
|
2114
2114
|
import { Database } from "bun:sqlite";
|
|
2115
|
-
import {
|
|
2115
|
+
import { existsSync as existsSync2, mkdirSync, rmSync } from "fs";
|
|
2116
2116
|
import { join as join2 } from "path";
|
|
2117
2117
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2118
2118
|
function getDbPath() {
|
|
2119
|
-
if (process.env["
|
|
2120
|
-
return process.env["
|
|
2119
|
+
if (process.env["HASNA_INSTRUCTIONS_DB_PATH"]) {
|
|
2120
|
+
return process.env["HASNA_INSTRUCTIONS_DB_PATH"];
|
|
2121
2121
|
}
|
|
2122
|
-
if (process.env["CONFIGS_DB_PATH"]) {
|
|
2123
|
-
return process.env["CONFIGS_DB_PATH"];
|
|
2124
|
-
}
|
|
2125
|
-
migrateDotfile();
|
|
2126
2122
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
2127
|
-
const dir = join2(home, ".hasna", "
|
|
2123
|
+
const dir = join2(home, ".hasna", "instructions");
|
|
2128
2124
|
mkdirSync(dir, { recursive: true });
|
|
2129
|
-
return join2(dir, "
|
|
2125
|
+
return join2(dir, "instructions.db");
|
|
2130
2126
|
}
|
|
2131
2127
|
function uuid() {
|
|
2132
2128
|
return randomUUID3();
|
|
@@ -2212,24 +2208,6 @@ function insertFeedback(input, db) {
|
|
|
2212
2208
|
const d = db || getDatabase();
|
|
2213
2209
|
d.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", [input.message, input.email ?? null, input.category ?? "general", input.version ?? null]);
|
|
2214
2210
|
}
|
|
2215
|
-
function migrateDotfile() {
|
|
2216
|
-
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
2217
|
-
const oldDirs = [join2(home, ".open-configs"), join2(home, ".configs")];
|
|
2218
|
-
const newDir = join2(home, ".hasna", "configs");
|
|
2219
|
-
if (existsSync2(newDir))
|
|
2220
|
-
return;
|
|
2221
|
-
for (const oldDir of oldDirs) {
|
|
2222
|
-
if (!existsSync2(oldDir))
|
|
2223
|
-
continue;
|
|
2224
|
-
try {
|
|
2225
|
-
if (!statSync(oldDir).isDirectory())
|
|
2226
|
-
continue;
|
|
2227
|
-
mkdirSync(join2(home, ".hasna"), { recursive: true });
|
|
2228
|
-
cpSync(oldDir, newDir, { recursive: true, force: false });
|
|
2229
|
-
return;
|
|
2230
|
-
} catch {}
|
|
2231
|
-
}
|
|
2232
|
-
}
|
|
2233
2211
|
var MIGRATIONS, _db = null;
|
|
2234
2212
|
var init_database = __esm(() => {
|
|
2235
2213
|
MIGRATIONS = [
|
|
@@ -3594,7 +3572,7 @@ var init_redact = __esm(() => {
|
|
|
3594
3572
|
});
|
|
3595
3573
|
|
|
3596
3574
|
// src/lib/sync-dir.ts
|
|
3597
|
-
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync2, statSync
|
|
3575
|
+
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
3598
3576
|
import { join as join4, relative } from "path";
|
|
3599
3577
|
import { homedir as homedir4 } from "os";
|
|
3600
3578
|
function shouldSkip(p) {
|
|
@@ -3605,7 +3583,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
3605
3583
|
const absDir = expandPath(dir);
|
|
3606
3584
|
if (!existsSync5(absDir))
|
|
3607
3585
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
3608
|
-
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) => join4(absDir, f)).filter((f) =>
|
|
3586
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) => join4(absDir, f)).filter((f) => statSync(f).isFile());
|
|
3609
3587
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
3610
3588
|
const home = homedir4();
|
|
3611
3589
|
const allConfigs = await store.listConfigs();
|
|
@@ -4508,7 +4486,7 @@ var init_package_manager_guard = __esm(() => {
|
|
|
4508
4486
|
];
|
|
4509
4487
|
});
|
|
4510
4488
|
|
|
4511
|
-
// node_modules/@hasna/events/dist/commander.js
|
|
4489
|
+
// node_modules/.pnpm/@hasna+events@0.1.13/node_modules/@hasna/events/dist/commander.js
|
|
4512
4490
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
4513
4491
|
import { existsSync } from "fs";
|
|
4514
4492
|
import { homedir } from "os";
|
|
@@ -4525,29 +4503,83 @@ function getPathValue(input, path) {
|
|
|
4525
4503
|
return;
|
|
4526
4504
|
}, input);
|
|
4527
4505
|
}
|
|
4528
|
-
function
|
|
4529
|
-
const
|
|
4530
|
-
|
|
4506
|
+
function getFieldValues(input, path) {
|
|
4507
|
+
const values = [];
|
|
4508
|
+
const push = (value) => {
|
|
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}$`);
|
|
4531
4535
|
}
|
|
4532
|
-
function matchString(value, matcher) {
|
|
4536
|
+
function matchString(value, matcher, options = {}) {
|
|
4533
4537
|
if (matcher === undefined)
|
|
4534
4538
|
return true;
|
|
4535
4539
|
if (value === undefined)
|
|
4536
4540
|
return false;
|
|
4537
4541
|
const matchers = Array.isArray(matcher) ? matcher : [matcher];
|
|
4538
|
-
return matchers.some((item) => wildcardToRegExp(item).test(value));
|
|
4542
|
+
return matchers.some((item) => wildcardToRegExp(item, options).test(value));
|
|
4539
4543
|
}
|
|
4540
4544
|
function matchRecord(input, matcher) {
|
|
4541
4545
|
if (!matcher)
|
|
4542
4546
|
return true;
|
|
4543
4547
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
4544
|
-
const
|
|
4545
|
-
|
|
4546
|
-
return matchString(actual === undefined ? undefined : String(actual), expected);
|
|
4547
|
-
}
|
|
4548
|
-
return actual === expected;
|
|
4548
|
+
const actualValues = getFieldValues(input, path);
|
|
4549
|
+
return matchField(actualValues, expected, path);
|
|
4549
4550
|
});
|
|
4550
4551
|
}
|
|
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
|
+
}
|
|
4551
4583
|
function eventMatchesFilter(event, filter) {
|
|
4552
4584
|
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);
|
|
4553
4585
|
}
|
|
@@ -4563,6 +4595,14 @@ var HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME";
|
|
|
4563
4595
|
function getEventsDataDir(override) {
|
|
4564
4596
|
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join(homedir(), ".hasna", "events");
|
|
4565
4597
|
}
|
|
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
|
+
|
|
4566
4606
|
class JsonEventsStore {
|
|
4567
4607
|
dataDir;
|
|
4568
4608
|
channelsPath;
|
|
@@ -4674,6 +4714,52 @@ class JsonEventsStore {
|
|
|
4674
4714
|
});
|
|
4675
4715
|
}
|
|
4676
4716
|
}
|
|
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
|
+
}
|
|
4677
4763
|
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
4678
4764
|
function buildSignatureBase(timestamp, body) {
|
|
4679
4765
|
return `${timestamp}.${body}`;
|
|
@@ -4899,7 +4985,7 @@ class EventsClient {
|
|
|
4899
4985
|
}
|
|
4900
4986
|
return deliveries;
|
|
4901
4987
|
}
|
|
4902
|
-
async
|
|
4988
|
+
async matchChannel(id, input = {}) {
|
|
4903
4989
|
const channel = await this.store.getChannel(id);
|
|
4904
4990
|
if (!channel)
|
|
4905
4991
|
throw new Error(`Channel not found: ${id}`);
|
|
@@ -4916,6 +5002,34 @@ class EventsClient {
|
|
|
4916
5002
|
time: input.time,
|
|
4917
5003
|
id: input.id
|
|
4918
5004
|
});
|
|
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
|
+
}
|
|
4919
5033
|
const eventForChannel = await this.applyRedaction(event, channel);
|
|
4920
5034
|
const result = await this.deliverWithRetry(eventForChannel, channel);
|
|
4921
5035
|
await this.store.appendDelivery(result);
|
|
@@ -5026,6 +5140,76 @@ function normalizeRetryPolicy(policy) {
|
|
|
5026
5140
|
multiplier: Math.max(1, policy?.multiplier ?? 2)
|
|
5027
5141
|
};
|
|
5028
5142
|
}
|
|
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
|
+
}
|
|
5029
5213
|
function parseJsonObject(value, fallback) {
|
|
5030
5214
|
if (!value)
|
|
5031
5215
|
return fallback;
|
|
@@ -5047,18 +5231,6 @@ function parseHeaders(values) {
|
|
|
5047
5231
|
}
|
|
5048
5232
|
return headers;
|
|
5049
5233
|
}
|
|
5050
|
-
function parseFilter(options) {
|
|
5051
|
-
const filter2 = {};
|
|
5052
|
-
if (options.source)
|
|
5053
|
-
filter2.source = options.source;
|
|
5054
|
-
if (options.type)
|
|
5055
|
-
filter2.type = options.type;
|
|
5056
|
-
if (options.subject)
|
|
5057
|
-
filter2.subject = options.subject;
|
|
5058
|
-
if (options.severity)
|
|
5059
|
-
filter2.severity = options.severity;
|
|
5060
|
-
return Object.keys(filter2).length > 0 ? [filter2] : undefined;
|
|
5061
|
-
}
|
|
5062
5234
|
function createClient(options) {
|
|
5063
5235
|
if (options.createClient)
|
|
5064
5236
|
return options.createClient();
|
|
@@ -5076,16 +5248,16 @@ function hasJsonOption(options) {
|
|
|
5076
5248
|
function wantsJson(actionOptions, command) {
|
|
5077
5249
|
return hasJsonOption(actionOptions) || hasJsonOption(command);
|
|
5078
5250
|
}
|
|
5079
|
-
function
|
|
5080
|
-
const
|
|
5081
|
-
|
|
5251
|
+
function registerChannelCommands(program, options) {
|
|
5252
|
+
const channels = program.command(options.channelsCommandName ?? "channels").description("Manage Hasna event channels");
|
|
5253
|
+
channels.command("add").description("Add or replace a channel").argument("<target>", "Webhook URL or command binary").requiredOption("--id <id>", "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("--data <path=value...>", "Event data field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard", collectValues, []).option("--metadata <path=value...>", "Event metadata field filter; string values, path!=value negatives, array-member matching, dot paths, * segment wildcard, ** recursive wildcard", collectValues, []).option("--data-json <path=json...>", "Event data field filter with typed JSON value; path!=json negatives supported", collectValues, []).option("--metadata-json <path=json...>", "Event metadata field filter with typed JSON value; path!=json negatives supported", collectValues, []).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) => {
|
|
5082
5254
|
const timestamp = new Date().toISOString();
|
|
5083
5255
|
const channel = {
|
|
5084
5256
|
id: actionOptions.id,
|
|
5085
5257
|
name: actionOptions.name,
|
|
5086
5258
|
enabled: !actionOptions.disabled,
|
|
5087
5259
|
transport: actionOptions.transport,
|
|
5088
|
-
filters:
|
|
5260
|
+
filters: parseFilterOptions(actionOptions),
|
|
5089
5261
|
retry: actionOptions.retryAttempts || actionOptions.retryBackoffMs ? { maxAttempts: actionOptions.retryAttempts, backoffMs: actionOptions.retryBackoffMs } : undefined,
|
|
5090
5262
|
redact: actionOptions.redact?.length ? { paths: actionOptions.redact } : undefined,
|
|
5091
5263
|
createdAt: timestamp,
|
|
@@ -5101,35 +5273,51 @@ function registerWebhookCommands(program, options) {
|
|
|
5101
5273
|
const saved = await createClient(options).addChannel(channel);
|
|
5102
5274
|
print(sanitizeChannelForOutput(saved), wantsJson(actionOptions, command), `Added ${saved.transport} channel ${saved.id}`);
|
|
5103
5275
|
});
|
|
5104
|
-
|
|
5105
|
-
const
|
|
5276
|
+
channels.command("list").description("List configured channels").option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
|
|
5277
|
+
const channels2 = await createClient(options).listChannels();
|
|
5106
5278
|
if (wantsJson(actionOptions, command)) {
|
|
5107
|
-
console.log(JSON.stringify(sanitizeChannelsForOutput(
|
|
5279
|
+
console.log(JSON.stringify(sanitizeChannelsForOutput(channels2), null, 2));
|
|
5108
5280
|
return;
|
|
5109
5281
|
}
|
|
5110
|
-
if (!
|
|
5282
|
+
if (!channels2.length) {
|
|
5111
5283
|
console.log("No channels configured.");
|
|
5112
5284
|
return;
|
|
5113
5285
|
}
|
|
5114
|
-
for (const channel of
|
|
5286
|
+
for (const channel of channels2) {
|
|
5115
5287
|
console.log(`${channel.id} ${channel.enabled ? "enabled" : "disabled"} ${channel.transport} ${channel.webhook?.url ?? channel.command?.command ?? channel.transport}`);
|
|
5116
5288
|
}
|
|
5117
5289
|
});
|
|
5118
|
-
|
|
5290
|
+
channels.command("status").description("Show events channel storage status").option("-j, --json", "Print JSON output", false).action(async (actionOptions, command) => {
|
|
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) => {
|
|
5119
5295
|
const removed = await createClient(options).removeChannel(id);
|
|
5120
5296
|
print({ removed }, wantsJson(actionOptions, command), removed ? `Removed ${id}` : `Channel not found: ${id}`);
|
|
5121
5297
|
});
|
|
5122
|
-
|
|
5298
|
+
channels.command("test").description("Send a test event to one channel").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 test delivery").option("--data <json>", "Event data JSON object").option("--metadata <json>", "Event metadata JSON object").option("--honor-filters", "Skip delivery when the sample event does not match channel filters", false).option("-j, --json", "Print JSON output", false).action(async (id, actionOptions, command) => {
|
|
5123
5299
|
const result = await createClient(options).testChannel(id, {
|
|
5124
|
-
source: options.source,
|
|
5300
|
+
source: actionOptions.source ?? options.source,
|
|
5125
5301
|
type: actionOptions.type,
|
|
5126
5302
|
subject: actionOptions.subject ?? id,
|
|
5127
5303
|
message: actionOptions.message,
|
|
5128
|
-
data: parseJsonObject(actionOptions.data, { test: true })
|
|
5129
|
-
|
|
5304
|
+
data: parseJsonObject(actionOptions.data, { test: true }),
|
|
5305
|
+
metadata: parseJsonObject(actionOptions.metadata, {})
|
|
5306
|
+
}, { honorFilters: actionOptions.honorFilters });
|
|
5130
5307
|
print(result, wantsJson(actionOptions, command), `${result.status}: ${result.channelId}`);
|
|
5131
5308
|
});
|
|
5132
|
-
|
|
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,
|
|
5312
|
+
type: actionOptions.type,
|
|
5313
|
+
subject: actionOptions.subject ?? id,
|
|
5314
|
+
message: actionOptions.message,
|
|
5315
|
+
data: parseJsonObject(actionOptions.data, { test: true }),
|
|
5316
|
+
metadata: parseJsonObject(actionOptions.metadata, {})
|
|
5317
|
+
});
|
|
5318
|
+
print(result, wantsJson(actionOptions, command), `${result.matched ? "matched" : "skipped"}: ${result.channelId}`);
|
|
5319
|
+
});
|
|
5320
|
+
return channels;
|
|
5133
5321
|
}
|
|
5134
5322
|
function registerEventCommands(program, options) {
|
|
5135
5323
|
const events = program.command(options.eventsCommandName ?? "events").description("Emit, list, and replay Hasna events");
|
|
@@ -5177,7 +5365,7 @@ function registerEventCommands(program, options) {
|
|
|
5177
5365
|
return events;
|
|
5178
5366
|
}
|
|
5179
5367
|
function registerEventsCommands(program, options) {
|
|
5180
|
-
|
|
5368
|
+
registerChannelCommands(program, options);
|
|
5181
5369
|
registerEventCommands(program, options);
|
|
5182
5370
|
}
|
|
5183
5371
|
function parseNumber(value) {
|
|
@@ -5345,7 +5533,7 @@ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join9, parse as
|
|
|
5345
5533
|
|
|
5346
5534
|
// src/lib/session-render.ts
|
|
5347
5535
|
import { createHash } from "crypto";
|
|
5348
|
-
import { existsSync as existsSync9, readFileSync as readFileSync5, realpathSync as realpathSync2, statSync as
|
|
5536
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5, realpathSync as realpathSync2, statSync as statSync2 } from "fs";
|
|
5349
5537
|
import { homedir as homedir5 } from "os";
|
|
5350
5538
|
import { basename as basename4, dirname as dirname2, extname as extname3, isAbsolute, join as join8, parse, posix, relative as relative2, resolve as resolve4 } from "path";
|
|
5351
5539
|
var CODEWITH_NATIVE_IMPORTS_ENV = "HASNA_CONFIGS_CODEWITH_NATIVE_IMPORTS";
|
|
@@ -6111,7 +6299,7 @@ function readIdentitySourcePath(sourcePath, baseDir, sourceId) {
|
|
|
6111
6299
|
}
|
|
6112
6300
|
return;
|
|
6113
6301
|
}
|
|
6114
|
-
const stat =
|
|
6302
|
+
const stat = statSync2(resolvedPath);
|
|
6115
6303
|
if (!stat.isFile()) {
|
|
6116
6304
|
throw new Error(`Identity instruction source path is not a file for ${sourceId}: ${sourcePath.path}`);
|
|
6117
6305
|
}
|
|
@@ -6748,14 +6936,12 @@ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
|
|
|
6748
6936
|
var PACKAGE_NAME = "@hasna/instructions";
|
|
6749
6937
|
var PACKAGE_VERSION = "0.3.0";
|
|
6750
6938
|
function activeDatabaseEnv() {
|
|
6751
|
-
if (process.env["
|
|
6752
|
-
return "
|
|
6753
|
-
if (process.env["CONFIGS_DB_PATH"])
|
|
6754
|
-
return "CONFIGS_DB_PATH";
|
|
6939
|
+
if (process.env["HASNA_INSTRUCTIONS_DB_PATH"])
|
|
6940
|
+
return "HASNA_INSTRUCTIONS_DB_PATH";
|
|
6755
6941
|
return null;
|
|
6756
6942
|
}
|
|
6757
6943
|
function configuredDatabaseKind() {
|
|
6758
|
-
const value = process.env["
|
|
6944
|
+
const value = process.env["HASNA_INSTRUCTIONS_DB_PATH"] ?? "";
|
|
6759
6945
|
return value === ":memory:" || value.startsWith("file::memory:") ? "memory" : "file";
|
|
6760
6946
|
}
|
|
6761
6947
|
function countBy(items, getValue) {
|
|
@@ -6829,8 +7015,7 @@ async function getConfigsStatus(store = resolveConfigStore()) {
|
|
|
6829
7015
|
},
|
|
6830
7016
|
env: {
|
|
6831
7017
|
database: {
|
|
6832
|
-
primary: "
|
|
6833
|
-
fallback: "CONFIGS_DB_PATH",
|
|
7018
|
+
primary: "HASNA_INSTRUCTIONS_DB_PATH",
|
|
6834
7019
|
active: activeDatabaseEnv(),
|
|
6835
7020
|
kind: configuredDatabaseKind()
|
|
6836
7021
|
}
|
|
@@ -7123,14 +7308,14 @@ program.command("list").alias("ls").description("List stored configs").option("-
|
|
|
7123
7308
|
tags: opts.tag ? [opts.tag] : undefined,
|
|
7124
7309
|
search: opts.search
|
|
7125
7310
|
});
|
|
7126
|
-
if (configs.length === 0) {
|
|
7127
|
-
console.log(chalk.dim("No configs found."));
|
|
7128
|
-
return;
|
|
7129
|
-
}
|
|
7130
7311
|
if (fmt === "json") {
|
|
7131
7312
|
printJson(configs);
|
|
7132
7313
|
return;
|
|
7133
7314
|
}
|
|
7315
|
+
if (configs.length === 0) {
|
|
7316
|
+
console.log(chalk.dim("No configs found."));
|
|
7317
|
+
return;
|
|
7318
|
+
}
|
|
7134
7319
|
const page = paginate(configs, { limit: opts.limit, cursor: opts.cursor });
|
|
7135
7320
|
if (fmt === "compact") {
|
|
7136
7321
|
printConfigRows(page.items);
|
|
@@ -7333,9 +7518,9 @@ program.command("import <file>").description("Import configs from a tar.gz bundl
|
|
|
7333
7518
|
});
|
|
7334
7519
|
program.command("whoami").description("Show setup summary").action(async () => {
|
|
7335
7520
|
const store = resolveConfigStore();
|
|
7336
|
-
const dbPath = isCloudMode() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1 (self_hosted)` : process.env["
|
|
7521
|
+
const dbPath = isCloudMode() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1 (self_hosted)` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join11(homedir7(), ".hasna", "instructions", "instructions.db");
|
|
7337
7522
|
const stats = await store.getConfigStats();
|
|
7338
|
-
console.log(chalk.bold("@hasna/
|
|
7523
|
+
console.log(chalk.bold("@hasna/instructions") + chalk.dim(" v" + pkg.version));
|
|
7339
7524
|
console.log(chalk.cyan(isCloudMode() ? "API:" : "DB:") + " " + dbPath);
|
|
7340
7525
|
console.log(chalk.cyan("Total configs:") + " " + (stats["total"] || 0));
|
|
7341
7526
|
console.log();
|
|
@@ -7359,14 +7544,14 @@ profileCmd.command("list").description("List all profiles").option("--brief", "c
|
|
|
7359
7544
|
const fmt = opts.json ? "json" : opts.verbose ? "table" : opts.brief ? "compact" : opts.format;
|
|
7360
7545
|
const store = resolveConfigStore();
|
|
7361
7546
|
const profiles = await store.listProfiles();
|
|
7362
|
-
if (profiles.length === 0) {
|
|
7363
|
-
console.log(chalk.dim("No profiles."));
|
|
7364
|
-
return;
|
|
7365
|
-
}
|
|
7366
7547
|
if (fmt === "json") {
|
|
7367
7548
|
printJson(profiles);
|
|
7368
7549
|
return;
|
|
7369
7550
|
}
|
|
7551
|
+
if (profiles.length === 0) {
|
|
7552
|
+
console.log(chalk.dim("No profiles."));
|
|
7553
|
+
return;
|
|
7554
|
+
}
|
|
7370
7555
|
const page = paginate(profiles, { limit: opts.limit, cursor: opts.cursor });
|
|
7371
7556
|
if (fmt === "compact")
|
|
7372
7557
|
console.log(`${pad("slug", 28)} ${pad("configs", 8)} ${pad("match", 36)} vars`);
|
|
@@ -7798,7 +7983,7 @@ program.command("package-manager-scan [paths...]").description("Scan package-man
|
|
|
7798
7983
|
}
|
|
7799
7984
|
});
|
|
7800
7985
|
var mcpCmd = program.command("mcp").description("Install/remove MCP server for AI agents");
|
|
7801
|
-
mcpCmd.command("install").alias("add").description("Install configs MCP server into an agent").option("--claude", "install into Claude Code").option("--codex", "install into Codex").option("--gemini", "install into Gemini").option("--all", "install into all agents").option("--profile <level>", "set
|
|
7986
|
+
mcpCmd.command("install").alias("add").description("Install configs MCP server into an agent").option("--claude", "install into Claude Code").option("--codex", "install into Codex").option("--gemini", "install into Gemini").option("--all", "install into all agents").option("--profile <level>", "set INSTRUCTIONS_PROFILE (minimal|standard|full)", "standard").action(async (opts) => {
|
|
7802
7987
|
const targets = opts.all ? ["claude", "codex", "gemini"] : [
|
|
7803
7988
|
...opts.claude ? ["claude"] : [],
|
|
7804
7989
|
...opts.codex ? ["codex"] : [],
|
|
@@ -7813,7 +7998,7 @@ mcpCmd.command("install").alias("add").description("Install configs MCP server i
|
|
|
7813
7998
|
const { vars } = await getMachineProfileContext({}, resolveConfigStore());
|
|
7814
7999
|
const mcpBinary = `${vars["BUN_BIN_DIR"]}/configs-mcp`;
|
|
7815
8000
|
if (target === "claude") {
|
|
7816
|
-
const cmd = opts.profile && opts.profile !== "full" ? ["claude", "mcp", "add", "--transport", "stdio", "--scope", "user", "configs", "--", "env", `
|
|
8001
|
+
const cmd = opts.profile && opts.profile !== "full" ? ["claude", "mcp", "add", "--transport", "stdio", "--scope", "user", "configs", "--", "env", `INSTRUCTIONS_PROFILE=${opts.profile}`, mcpBinary] : ["claude", "mcp", "add", "--transport", "stdio", "--scope", "user", "configs", "--", mcpBinary];
|
|
7817
8002
|
const proc = Bun.spawn(cmd, { stdout: "inherit", stderr: "inherit" });
|
|
7818
8003
|
await proc.exited;
|
|
7819
8004
|
console.log(chalk.green("\u2713") + " Installed into Claude Code");
|
|
@@ -7870,7 +8055,7 @@ program.command("init").description("First-time setup: sync all known configs, c
|
|
|
7870
8055
|
await store.reset();
|
|
7871
8056
|
console.log(chalk.dim("Reset local store."));
|
|
7872
8057
|
}
|
|
7873
|
-
console.log(chalk.bold(`@hasna/
|
|
8058
|
+
console.log(chalk.bold(`@hasna/instructions \u2014 initializing
|
|
7874
8059
|
`));
|
|
7875
8060
|
const result = await syncKnown({ store });
|
|
7876
8061
|
console.log(chalk.green("\u2713") + ` Synced: +${result.added} updated:${result.updated} unchanged:${result.unchanged}`);
|
|
@@ -7914,7 +8099,7 @@ DB stats:`));
|
|
|
7914
8099
|
if (count > 0)
|
|
7915
8100
|
console.log(` ${key.padEnd(18)} ${count}`);
|
|
7916
8101
|
}
|
|
7917
|
-
const location = isCloudMode() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1 (self_hosted)` : process.env["
|
|
8102
|
+
const location = isCloudMode() ? `${process.env["HASNA_INSTRUCTIONS_API_URL"]}/v1 (self_hosted)` : process.env["HASNA_INSTRUCTIONS_DB_PATH"] || join11(homedir7(), ".hasna", "instructions", "instructions.db");
|
|
7918
8103
|
console.log(chalk.dim(`
|
|
7919
8104
|
${isCloudMode() ? "API" : "DB"}: ${location}`));
|
|
7920
8105
|
});
|
|
@@ -7924,7 +8109,7 @@ program.command("status").description("Health check: total configs, drift from d
|
|
|
7924
8109
|
printJson(status);
|
|
7925
8110
|
return;
|
|
7926
8111
|
}
|
|
7927
|
-
console.log(chalk.bold("@hasna/
|
|
8112
|
+
console.log(chalk.bold("@hasna/instructions") + chalk.dim(` v${pkg.version}`));
|
|
7928
8113
|
console.log(chalk.cyan("Database:") + ` ${status.env.database.kind} (${status.env.database.active ?? "default"})`);
|
|
7929
8114
|
console.log(chalk.cyan("Total:") + ` ${status.counts.configs.total} configs
|
|
7930
8115
|
`);
|
|
@@ -7935,7 +8120,7 @@ program.command("status").description("Health check: total configs, drift from d
|
|
|
7935
8120
|
});
|
|
7936
8121
|
program.command("backup").description("Export configs to a timestamped backup file").action(async () => {
|
|
7937
8122
|
const { mkdirSync: mk } = await import("fs");
|
|
7938
|
-
const backupDir = join11(homedir7(), ".hasna", "
|
|
8123
|
+
const backupDir = join11(homedir7(), ".hasna", "instructions", "backups");
|
|
7939
8124
|
mk(backupDir, { recursive: true });
|
|
7940
8125
|
const ts = new Date().toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 19);
|
|
7941
8126
|
const outPath = join11(backupDir, `configs-${ts}.tar.gz`);
|
|
@@ -8086,7 +8271,7 @@ program.command("watch").description("Watch known config files for changes and a
|
|
|
8086
8271
|
const interval = Number(opts.interval);
|
|
8087
8272
|
const { statSync: st } = await import("fs");
|
|
8088
8273
|
const { expandPath: expandPath2 } = await Promise.resolve().then(() => (init_apply(), exports_apply));
|
|
8089
|
-
console.log(chalk.bold("@hasna/
|
|
8274
|
+
console.log(chalk.bold("@hasna/instructions watch") + chalk.dim(` \u2014 polling every ${interval}ms`));
|
|
8090
8275
|
console.log(chalk.dim(`Watching known config files for changes\u2026
|
|
8091
8276
|
`));
|
|
8092
8277
|
const mtimes = new Map;
|
|
@@ -8246,7 +8431,7 @@ program.command("bootstrap").description("Install the full @hasna ecosystem: CLI
|
|
|
8246
8431
|
{ name: "@hasna/assistants", bin: "assistants", mcp: "assistants-mcp" },
|
|
8247
8432
|
{ name: "@hasna/brains", bin: "brains", mcp: "brains-mcp" }
|
|
8248
8433
|
];
|
|
8249
|
-
console.log(chalk.bold("@hasna/
|
|
8434
|
+
console.log(chalk.bold("@hasna/instructions bootstrap") + chalk.dim(` \u2014 installing ${packages.length} ecosystem packages
|
|
8250
8435
|
`));
|
|
8251
8436
|
console.log(chalk.cyan("Installing CLI tools:"));
|
|
8252
8437
|
for (const pkg2 of packages) {
|
|
@@ -8306,7 +8491,7 @@ program.command("push").description("Alias for sync --to-disk (write DB configs
|
|
|
8306
8491
|
});
|
|
8307
8492
|
program.command("update").description("Check for updates and install latest version").option("--check", "only check, don't install").action(async (opts) => {
|
|
8308
8493
|
try {
|
|
8309
|
-
const proc = Bun.spawn(["npm", "view", "@hasna/
|
|
8494
|
+
const proc = Bun.spawn(["npm", "view", "@hasna/instructions", "version"], { stdout: "pipe", stderr: "pipe" });
|
|
8310
8495
|
const latest = (await new Response(proc.stdout).text()).trim();
|
|
8311
8496
|
await proc.exited;
|
|
8312
8497
|
if (latest === pkg.version) {
|
|
@@ -8315,7 +8500,7 @@ program.command("update").description("Check for updates and install latest vers
|
|
|
8315
8500
|
console.log(`Current: ${chalk.dim(pkg.version)} \u2192 Latest: ${chalk.green(latest)}`);
|
|
8316
8501
|
if (!opts.check) {
|
|
8317
8502
|
console.log(chalk.dim("Installing..."));
|
|
8318
|
-
const install = Bun.spawn(["bun", "install", "-g", `@hasna/
|
|
8503
|
+
const install = Bun.spawn(["bun", "install", "-g", `@hasna/instructions@${latest}`], { stdout: "inherit", stderr: "inherit" });
|
|
8319
8504
|
await install.exited;
|
|
8320
8505
|
console.log(chalk.green("\u2713") + ` Updated to ${latest}`);
|
|
8321
8506
|
}
|
package/dist/db/database.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare function resetDatabase(): void;
|
|
|
7
7
|
/**
|
|
8
8
|
* Destroy the on-disk local database: close the handle and delete the db file
|
|
9
9
|
* plus its WAL/SHM sidecars. Used by `init --force`. Resolves the path from the
|
|
10
|
-
* db module (honoring
|
|
10
|
+
* db module (honoring HASNA_INSTRUCTIONS_DB_PATH); a no-op for the
|
|
11
11
|
* in-memory (`:memory:`) database. Local-only — the CloudConfigStore never calls
|
|
12
12
|
* this (destroying the shared cloud store from a client is forbidden).
|
|
13
13
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/db/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAetC,wBAAgB,IAAI,IAAI,MAAM,CAE7B;AAED,wBAAgB,GAAG,IAAI,MAAM,CAE5B;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5C;AAyED,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,CAqBnD;AAED,wBAAgB,aAAa,IAAI,IAAI,CAKpC;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAOzC;AAsDD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAMxE"}
|
package/dist/index.js
CHANGED
|
@@ -73,21 +73,17 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
73
73
|
|
|
74
74
|
// src/db/database.ts
|
|
75
75
|
import { Database } from "bun:sqlite";
|
|
76
|
-
import {
|
|
76
|
+
import { existsSync, mkdirSync, rmSync } from "fs";
|
|
77
77
|
import { join } from "path";
|
|
78
78
|
import { randomUUID } from "crypto";
|
|
79
79
|
function getDbPath() {
|
|
80
|
-
if (process.env["
|
|
81
|
-
return process.env["
|
|
80
|
+
if (process.env["HASNA_INSTRUCTIONS_DB_PATH"]) {
|
|
81
|
+
return process.env["HASNA_INSTRUCTIONS_DB_PATH"];
|
|
82
82
|
}
|
|
83
|
-
if (process.env["CONFIGS_DB_PATH"]) {
|
|
84
|
-
return process.env["CONFIGS_DB_PATH"];
|
|
85
|
-
}
|
|
86
|
-
migrateDotfile();
|
|
87
83
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
88
|
-
const dir = join(home, ".hasna", "
|
|
84
|
+
const dir = join(home, ".hasna", "instructions");
|
|
89
85
|
mkdirSync(dir, { recursive: true });
|
|
90
|
-
return join(dir, "
|
|
86
|
+
return join(dir, "instructions.db");
|
|
91
87
|
}
|
|
92
88
|
function uuid() {
|
|
93
89
|
return randomUUID();
|
|
@@ -242,24 +238,6 @@ function insertFeedback(input, db) {
|
|
|
242
238
|
const d = db || getDatabase();
|
|
243
239
|
d.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", [input.message, input.email ?? null, input.category ?? "general", input.version ?? null]);
|
|
244
240
|
}
|
|
245
|
-
function migrateDotfile() {
|
|
246
|
-
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
247
|
-
const oldDirs = [join(home, ".open-configs"), join(home, ".configs")];
|
|
248
|
-
const newDir = join(home, ".hasna", "configs");
|
|
249
|
-
if (existsSync(newDir))
|
|
250
|
-
return;
|
|
251
|
-
for (const oldDir of oldDirs) {
|
|
252
|
-
if (!existsSync(oldDir))
|
|
253
|
-
continue;
|
|
254
|
-
try {
|
|
255
|
-
if (!statSync(oldDir).isDirectory())
|
|
256
|
-
continue;
|
|
257
|
-
mkdirSync(join(home, ".hasna"), { recursive: true });
|
|
258
|
-
cpSync(oldDir, newDir, { recursive: true, force: false });
|
|
259
|
-
return;
|
|
260
|
-
} catch {}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
241
|
|
|
264
242
|
// src/db/configs.ts
|
|
265
243
|
function rowToConfig(row) {
|
|
@@ -1510,14 +1488,12 @@ function hasSecrets(content, format) {
|
|
|
1510
1488
|
var PACKAGE_NAME = "@hasna/instructions";
|
|
1511
1489
|
var PACKAGE_VERSION = "0.3.0";
|
|
1512
1490
|
function activeDatabaseEnv() {
|
|
1513
|
-
if (process.env["
|
|
1514
|
-
return "
|
|
1515
|
-
if (process.env["CONFIGS_DB_PATH"])
|
|
1516
|
-
return "CONFIGS_DB_PATH";
|
|
1491
|
+
if (process.env["HASNA_INSTRUCTIONS_DB_PATH"])
|
|
1492
|
+
return "HASNA_INSTRUCTIONS_DB_PATH";
|
|
1517
1493
|
return null;
|
|
1518
1494
|
}
|
|
1519
1495
|
function configuredDatabaseKind() {
|
|
1520
|
-
const value = process.env["
|
|
1496
|
+
const value = process.env["HASNA_INSTRUCTIONS_DB_PATH"] ?? "";
|
|
1521
1497
|
return value === ":memory:" || value.startsWith("file::memory:") ? "memory" : "file";
|
|
1522
1498
|
}
|
|
1523
1499
|
function countBy(items, getValue) {
|
|
@@ -1591,8 +1567,7 @@ async function getConfigsStatus(store = resolveConfigStore()) {
|
|
|
1591
1567
|
},
|
|
1592
1568
|
env: {
|
|
1593
1569
|
database: {
|
|
1594
|
-
primary: "
|
|
1595
|
-
fallback: "CONFIGS_DB_PATH",
|
|
1570
|
+
primary: "HASNA_INSTRUCTIONS_DB_PATH",
|
|
1596
1571
|
active: activeDatabaseEnv(),
|
|
1597
1572
|
kind: configuredDatabaseKind()
|
|
1598
1573
|
}
|
|
@@ -1697,7 +1672,7 @@ var PG_MIGRATIONS = [
|
|
|
1697
1672
|
];
|
|
1698
1673
|
// src/lib/session-render.ts
|
|
1699
1674
|
import { createHash } from "crypto";
|
|
1700
|
-
import { existsSync as existsSync5, readFileSync as readFileSync3, realpathSync as realpathSync2, statSync
|
|
1675
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, realpathSync as realpathSync2, statSync } from "fs";
|
|
1701
1676
|
import { homedir as homedir3 } from "os";
|
|
1702
1677
|
import { basename as basename3, dirname as dirname2, extname as extname2, isAbsolute, join as join3, parse, posix, relative, resolve as resolve2 } from "path";
|
|
1703
1678
|
var CODEWITH_NATIVE_IMPORTS_ENV = "HASNA_CONFIGS_CODEWITH_NATIVE_IMPORTS";
|
|
@@ -2463,7 +2438,7 @@ function readIdentitySourcePath(sourcePath, baseDir, sourceId) {
|
|
|
2463
2438
|
}
|
|
2464
2439
|
return;
|
|
2465
2440
|
}
|
|
2466
|
-
const stat =
|
|
2441
|
+
const stat = statSync(resolvedPath);
|
|
2467
2442
|
if (!stat.isFile()) {
|
|
2468
2443
|
throw new Error(`Identity instruction source path is not a file for ${sourceId}: ${sourcePath.path}`);
|
|
2469
2444
|
}
|
|
@@ -3101,7 +3076,7 @@ import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as
|
|
|
3101
3076
|
import { basename as basename4, extname as extname3, join as join6 } from "path";
|
|
3102
3077
|
|
|
3103
3078
|
// src/lib/sync-dir.ts
|
|
3104
|
-
import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync5, statSync as
|
|
3079
|
+
import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
3105
3080
|
import { join as join5, relative as relative3 } from "path";
|
|
3106
3081
|
import { homedir as homedir4 } from "os";
|
|
3107
3082
|
var SKIP = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
|
|
@@ -3113,7 +3088,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
3113
3088
|
const absDir = expandPath(dir);
|
|
3114
3089
|
if (!existsSync7(absDir))
|
|
3115
3090
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
3116
|
-
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) => join5(absDir, f)).filter((f) =>
|
|
3091
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) => join5(absDir, f)).filter((f) => statSync2(f).isFile());
|
|
3117
3092
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
3118
3093
|
const home = homedir4();
|
|
3119
3094
|
const allConfigs = await store.listConfigs();
|
package/dist/mcp/index.js
CHANGED
|
@@ -49,21 +49,17 @@ var init_types = __esm(() => {
|
|
|
49
49
|
|
|
50
50
|
// src/db/database.ts
|
|
51
51
|
import { Database } from "bun:sqlite";
|
|
52
|
-
import {
|
|
52
|
+
import { existsSync, mkdirSync, rmSync } from "fs";
|
|
53
53
|
import { join } from "path";
|
|
54
54
|
import { randomUUID } from "crypto";
|
|
55
55
|
function getDbPath() {
|
|
56
|
-
if (process.env["
|
|
57
|
-
return process.env["
|
|
56
|
+
if (process.env["HASNA_INSTRUCTIONS_DB_PATH"]) {
|
|
57
|
+
return process.env["HASNA_INSTRUCTIONS_DB_PATH"];
|
|
58
58
|
}
|
|
59
|
-
if (process.env["CONFIGS_DB_PATH"]) {
|
|
60
|
-
return process.env["CONFIGS_DB_PATH"];
|
|
61
|
-
}
|
|
62
|
-
migrateDotfile();
|
|
63
59
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
64
|
-
const dir = join(home, ".hasna", "
|
|
60
|
+
const dir = join(home, ".hasna", "instructions");
|
|
65
61
|
mkdirSync(dir, { recursive: true });
|
|
66
|
-
return join(dir, "
|
|
62
|
+
return join(dir, "instructions.db");
|
|
67
63
|
}
|
|
68
64
|
function uuid() {
|
|
69
65
|
return randomUUID();
|
|
@@ -149,24 +145,6 @@ function insertFeedback(input, db) {
|
|
|
149
145
|
const d = db || getDatabase();
|
|
150
146
|
d.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", [input.message, input.email ?? null, input.category ?? "general", input.version ?? null]);
|
|
151
147
|
}
|
|
152
|
-
function migrateDotfile() {
|
|
153
|
-
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
154
|
-
const oldDirs = [join(home, ".open-configs"), join(home, ".configs")];
|
|
155
|
-
const newDir = join(home, ".hasna", "configs");
|
|
156
|
-
if (existsSync(newDir))
|
|
157
|
-
return;
|
|
158
|
-
for (const oldDir of oldDirs) {
|
|
159
|
-
if (!existsSync(oldDir))
|
|
160
|
-
continue;
|
|
161
|
-
try {
|
|
162
|
-
if (!statSync(oldDir).isDirectory())
|
|
163
|
-
continue;
|
|
164
|
-
mkdirSync(join(home, ".hasna"), { recursive: true });
|
|
165
|
-
cpSync(oldDir, newDir, { recursive: true, force: false });
|
|
166
|
-
return;
|
|
167
|
-
} catch {}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
148
|
var MIGRATIONS, _db = null;
|
|
171
149
|
var init_database = __esm(() => {
|
|
172
150
|
MIGRATIONS = [
|
|
@@ -1974,7 +1952,7 @@ var init_sync = __esm(() => {
|
|
|
1974
1952
|
});
|
|
1975
1953
|
|
|
1976
1954
|
// src/lib/sync-dir.ts
|
|
1977
|
-
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync
|
|
1955
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync } from "fs";
|
|
1978
1956
|
import { join as join4, relative } from "path";
|
|
1979
1957
|
import { homedir as homedir3 } from "os";
|
|
1980
1958
|
function shouldSkip(p) {
|
|
@@ -1985,7 +1963,7 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
1985
1963
|
const absDir = expandPath(dir);
|
|
1986
1964
|
if (!existsSync5(absDir))
|
|
1987
1965
|
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
1988
|
-
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join4(absDir, f)).filter((f) =>
|
|
1966
|
+
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync2(absDir).map((f) => join4(absDir, f)).filter((f) => statSync(f).isFile());
|
|
1989
1967
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
1990
1968
|
const home = homedir3();
|
|
1991
1969
|
const allConfigs = await store.listConfigs();
|
|
@@ -2062,7 +2040,7 @@ var init_sync_dir = __esm(() => {
|
|
|
2062
2040
|
var require_package = __commonJS((exports, module) => {
|
|
2063
2041
|
module.exports = {
|
|
2064
2042
|
name: "@hasna/instructions",
|
|
2065
|
-
version: "0.4.
|
|
2043
|
+
version: "0.4.3",
|
|
2066
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.",
|
|
2067
2045
|
type: "module",
|
|
2068
2046
|
main: "dist/index.js",
|
|
@@ -2286,7 +2264,7 @@ var PROFILES = {
|
|
|
2286
2264
|
standard: ["list_configs", "get_config", "create_config", "update_config", "apply_config", "sync_known", "get_status", "render_template", "scan_secrets", "list_profiles", "apply_profile", "search_tools", "describe_tools"],
|
|
2287
2265
|
full: []
|
|
2288
2266
|
};
|
|
2289
|
-
var activeProfile = process.env["
|
|
2267
|
+
var activeProfile = process.env["INSTRUCTIONS_PROFILE"] || "full";
|
|
2290
2268
|
var profileFilter = PROFILES[activeProfile];
|
|
2291
2269
|
var ALL_LEAN_TOOLS = [
|
|
2292
2270
|
{ name: "list_configs", inputSchema: { type: "object", properties: { category: { type: "string" }, agent: { type: "string" }, kind: { type: "string" }, search: { type: "string" }, limit: { type: "number" }, cursor: { type: "number" }, verbose: { type: "boolean" } } } },
|
|
@@ -2466,7 +2444,7 @@ function buildServer() {
|
|
|
2466
2444
|
drifted,
|
|
2467
2445
|
drifted_configs: driftedSlugs.slice(0, 5),
|
|
2468
2446
|
missing,
|
|
2469
|
-
db_path: process.env["
|
|
2447
|
+
db_path: process.env["HASNA_INSTRUCTIONS_DB_PATH"] || "~/.hasna/instructions/instructions.db"
|
|
2470
2448
|
});
|
|
2471
2449
|
}
|
|
2472
2450
|
case "sync_known": {
|
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/hono/dist/compose.js
|
|
5
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/compose.js
|
|
6
6
|
var compose = (middleware, onError, onNotFound) => {
|
|
7
7
|
return (context, next) => {
|
|
8
8
|
let index = -1;
|
|
@@ -46,21 +46,39 @@ var compose = (middleware, onError, onNotFound) => {
|
|
|
46
46
|
};
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
// node_modules/hono/dist/request/constants.js
|
|
49
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/request/constants.js
|
|
50
50
|
var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
|
|
51
51
|
|
|
52
|
-
// node_modules/hono/dist/utils/
|
|
52
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/utils/buffer.js
|
|
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);
|
|
53
64
|
var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
|
|
54
65
|
const { all = false, dot = false } = options;
|
|
55
|
-
const headers = request
|
|
66
|
+
const headers = isRawRequest(request) ? request.headers : request.raw.headers;
|
|
56
67
|
const contentType = headers.get("Content-Type");
|
|
57
|
-
|
|
68
|
+
const mediaType = contentType?.split(";")[0].trim().toLowerCase();
|
|
69
|
+
if (mediaType === "multipart/form-data" || mediaType === "application/x-www-form-urlencoded") {
|
|
58
70
|
return parseFormData(request, { all, dot });
|
|
59
71
|
}
|
|
60
72
|
return {};
|
|
61
73
|
};
|
|
62
74
|
async function parseFormData(request, options) {
|
|
63
|
-
const
|
|
75
|
+
const headers = isRawRequest(request) ? request.headers : request.raw.headers;
|
|
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;
|
|
64
82
|
if (formData) {
|
|
65
83
|
return convertFormDataToBodyData(formData, options);
|
|
66
84
|
}
|
|
@@ -120,7 +138,7 @@ var handleParsingNestedValues = (form, key, value) => {
|
|
|
120
138
|
});
|
|
121
139
|
};
|
|
122
140
|
|
|
123
|
-
// node_modules/hono/dist/utils/url.js
|
|
141
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/utils/url.js
|
|
124
142
|
var splitPath = (path) => {
|
|
125
143
|
const paths = path.split("/");
|
|
126
144
|
if (paths[0] === "") {
|
|
@@ -320,7 +338,7 @@ var getQueryParams = (url, key) => {
|
|
|
320
338
|
};
|
|
321
339
|
var decodeURIComponent_ = decodeURIComponent;
|
|
322
340
|
|
|
323
|
-
// node_modules/hono/dist/request.js
|
|
341
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/request.js
|
|
324
342
|
var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
|
|
325
343
|
var HonoRequest = class {
|
|
326
344
|
raw;
|
|
@@ -402,6 +420,9 @@ var HonoRequest = class {
|
|
|
402
420
|
arrayBuffer() {
|
|
403
421
|
return this.#cachedBody("arrayBuffer");
|
|
404
422
|
}
|
|
423
|
+
bytes() {
|
|
424
|
+
return this.#cachedBody("arrayBuffer").then((buffer) => new Uint8Array(buffer));
|
|
425
|
+
}
|
|
405
426
|
blob() {
|
|
406
427
|
return this.#cachedBody("blob");
|
|
407
428
|
}
|
|
@@ -431,7 +452,7 @@ var HonoRequest = class {
|
|
|
431
452
|
}
|
|
432
453
|
};
|
|
433
454
|
|
|
434
|
-
// node_modules/hono/dist/utils/html.js
|
|
455
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/utils/html.js
|
|
435
456
|
var HtmlEscapedCallbackPhase = {
|
|
436
457
|
Stringify: 1,
|
|
437
458
|
BeforeStream: 2,
|
|
@@ -469,7 +490,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
|
|
|
469
490
|
}
|
|
470
491
|
};
|
|
471
492
|
|
|
472
|
-
// node_modules/hono/dist/context.js
|
|
493
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/context.js
|
|
473
494
|
var TEXT_PLAIN = "text/plain; charset=UTF-8";
|
|
474
495
|
var setDefaultContentType = (contentType, headers) => {
|
|
475
496
|
return {
|
|
@@ -636,7 +657,7 @@ var Context = class {
|
|
|
636
657
|
};
|
|
637
658
|
};
|
|
638
659
|
|
|
639
|
-
// node_modules/hono/dist/router.js
|
|
660
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/router.js
|
|
640
661
|
var METHOD_NAME_ALL = "ALL";
|
|
641
662
|
var METHOD_NAME_ALL_LOWERCASE = "all";
|
|
642
663
|
var METHODS = ["get", "post", "put", "delete", "options", "patch"];
|
|
@@ -644,10 +665,10 @@ var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is
|
|
|
644
665
|
var UnsupportedPathError = class extends Error {
|
|
645
666
|
};
|
|
646
667
|
|
|
647
|
-
// node_modules/hono/dist/utils/constants.js
|
|
668
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/utils/constants.js
|
|
648
669
|
var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
|
|
649
670
|
|
|
650
|
-
// node_modules/hono/dist/hono-base.js
|
|
671
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/hono-base.js
|
|
651
672
|
var notFoundHandler = (c) => {
|
|
652
673
|
return c.text("404 Not Found", 404);
|
|
653
674
|
};
|
|
@@ -738,7 +759,7 @@ var Hono = class _Hono {
|
|
|
738
759
|
handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;
|
|
739
760
|
handler[COMPOSED_HANDLER] = r.handler;
|
|
740
761
|
}
|
|
741
|
-
subApp.#addRoute(r.method, r.path, handler);
|
|
762
|
+
subApp.#addRoute(r.method, r.path, handler, r.basePath);
|
|
742
763
|
});
|
|
743
764
|
return this;
|
|
744
765
|
}
|
|
@@ -785,7 +806,7 @@ var Hono = class _Hono {
|
|
|
785
806
|
const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
|
|
786
807
|
return (request) => {
|
|
787
808
|
const url = new URL(request.url);
|
|
788
|
-
url.pathname =
|
|
809
|
+
url.pathname = this.getPath(request).slice(pathPrefixLength) || "/";
|
|
789
810
|
return new Request(url, request);
|
|
790
811
|
};
|
|
791
812
|
})();
|
|
@@ -799,10 +820,15 @@ var Hono = class _Hono {
|
|
|
799
820
|
this.#addRoute(METHOD_NAME_ALL, mergePath(path, "*"), handler);
|
|
800
821
|
return this;
|
|
801
822
|
}
|
|
802
|
-
#addRoute(method, path, handler) {
|
|
823
|
+
#addRoute(method, path, handler, baseRoutePath) {
|
|
803
824
|
method = method.toUpperCase();
|
|
804
825
|
path = mergePath(this._basePath, path);
|
|
805
|
-
const r = {
|
|
826
|
+
const r = {
|
|
827
|
+
basePath: baseRoutePath !== undefined ? mergePath(this._basePath, baseRoutePath) : this._basePath,
|
|
828
|
+
path,
|
|
829
|
+
method,
|
|
830
|
+
handler
|
|
831
|
+
};
|
|
806
832
|
this.router.add(method, path, [handler, r]);
|
|
807
833
|
this.routes.push(r);
|
|
808
834
|
}
|
|
@@ -866,7 +892,7 @@ var Hono = class _Hono {
|
|
|
866
892
|
};
|
|
867
893
|
};
|
|
868
894
|
|
|
869
|
-
// node_modules/hono/dist/router/reg-exp-router/matcher.js
|
|
895
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/router/reg-exp-router/matcher.js
|
|
870
896
|
var emptyParam = [];
|
|
871
897
|
function match(method, path) {
|
|
872
898
|
const matchers = this.buildAllMatchers();
|
|
@@ -887,7 +913,7 @@ function match(method, path) {
|
|
|
887
913
|
return match2(method, path);
|
|
888
914
|
}
|
|
889
915
|
|
|
890
|
-
// node_modules/hono/dist/router/reg-exp-router/node.js
|
|
916
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/router/reg-exp-router/node.js
|
|
891
917
|
var LABEL_REG_EXP_STR = "[^/]+";
|
|
892
918
|
var ONLY_WILDCARD_REG_EXP_STR = ".*";
|
|
893
919
|
var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
|
|
@@ -991,7 +1017,7 @@ var Node = class _Node {
|
|
|
991
1017
|
}
|
|
992
1018
|
};
|
|
993
1019
|
|
|
994
|
-
// node_modules/hono/dist/router/reg-exp-router/trie.js
|
|
1020
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/router/reg-exp-router/trie.js
|
|
995
1021
|
var Trie = class {
|
|
996
1022
|
#context = { varIndex: 0 };
|
|
997
1023
|
#root = new Node;
|
|
@@ -1047,7 +1073,7 @@ var Trie = class {
|
|
|
1047
1073
|
}
|
|
1048
1074
|
};
|
|
1049
1075
|
|
|
1050
|
-
// node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1076
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1051
1077
|
var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
|
|
1052
1078
|
var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
|
|
1053
1079
|
function buildWildcardRegExp(path) {
|
|
@@ -1212,7 +1238,7 @@ var RegExpRouter = class {
|
|
|
1212
1238
|
}
|
|
1213
1239
|
};
|
|
1214
1240
|
|
|
1215
|
-
// node_modules/hono/dist/router/reg-exp-router/prepared-router.js
|
|
1241
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/router/reg-exp-router/prepared-router.js
|
|
1216
1242
|
var PreparedRegExpRouter = class {
|
|
1217
1243
|
name = "PreparedRegExpRouter";
|
|
1218
1244
|
#matchers;
|
|
@@ -1284,7 +1310,7 @@ var PreparedRegExpRouter = class {
|
|
|
1284
1310
|
match = match;
|
|
1285
1311
|
};
|
|
1286
1312
|
|
|
1287
|
-
// node_modules/hono/dist/router/smart-router/router.js
|
|
1313
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/router/smart-router/router.js
|
|
1288
1314
|
var SmartRouter = class {
|
|
1289
1315
|
name = "SmartRouter";
|
|
1290
1316
|
#routers = [];
|
|
@@ -1339,7 +1365,7 @@ var SmartRouter = class {
|
|
|
1339
1365
|
}
|
|
1340
1366
|
};
|
|
1341
1367
|
|
|
1342
|
-
// node_modules/hono/dist/router/trie-router/node.js
|
|
1368
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/router/trie-router/node.js
|
|
1343
1369
|
var emptyParams = /* @__PURE__ */ Object.create(null);
|
|
1344
1370
|
var hasChildren = (children) => {
|
|
1345
1371
|
for (const _ in children) {
|
|
@@ -1508,7 +1534,7 @@ var Node2 = class _Node2 {
|
|
|
1508
1534
|
}
|
|
1509
1535
|
};
|
|
1510
1536
|
|
|
1511
|
-
// node_modules/hono/dist/router/trie-router/router.js
|
|
1537
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/router/trie-router/router.js
|
|
1512
1538
|
var TrieRouter = class {
|
|
1513
1539
|
name = "TrieRouter";
|
|
1514
1540
|
#node;
|
|
@@ -1530,7 +1556,7 @@ var TrieRouter = class {
|
|
|
1530
1556
|
}
|
|
1531
1557
|
};
|
|
1532
1558
|
|
|
1533
|
-
// node_modules/hono/dist/hono.js
|
|
1559
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/hono.js
|
|
1534
1560
|
var Hono2 = class extends Hono {
|
|
1535
1561
|
constructor(options = {}) {
|
|
1536
1562
|
super(options);
|
|
@@ -1540,24 +1566,18 @@ var Hono2 = class extends Hono {
|
|
|
1540
1566
|
}
|
|
1541
1567
|
};
|
|
1542
1568
|
|
|
1543
|
-
// node_modules/hono/dist/middleware/cors/index.js
|
|
1569
|
+
// node_modules/.pnpm/hono@4.12.28/node_modules/hono/dist/middleware/cors/index.js
|
|
1544
1570
|
var cors = (options) => {
|
|
1545
|
-
const
|
|
1571
|
+
const opts = {
|
|
1546
1572
|
origin: "*",
|
|
1547
1573
|
allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"],
|
|
1548
1574
|
allowHeaders: [],
|
|
1549
|
-
exposeHeaders: []
|
|
1550
|
-
};
|
|
1551
|
-
const opts = {
|
|
1552
|
-
...defaults,
|
|
1575
|
+
exposeHeaders: [],
|
|
1553
1576
|
...options
|
|
1554
1577
|
};
|
|
1555
1578
|
const findAllowOrigin = ((optsOrigin) => {
|
|
1556
1579
|
if (typeof optsOrigin === "string") {
|
|
1557
1580
|
if (optsOrigin === "*") {
|
|
1558
|
-
if (opts.credentials) {
|
|
1559
|
-
return (origin) => origin || null;
|
|
1560
|
-
}
|
|
1561
1581
|
return () => optsOrigin;
|
|
1562
1582
|
} else {
|
|
1563
1583
|
return (origin) => optsOrigin === origin ? origin : null;
|
|
@@ -1592,7 +1612,7 @@ var cors = (options) => {
|
|
|
1592
1612
|
set("Access-Control-Expose-Headers", opts.exposeHeaders.join(","));
|
|
1593
1613
|
}
|
|
1594
1614
|
if (c.req.method === "OPTIONS") {
|
|
1595
|
-
if (opts.origin !== "*"
|
|
1615
|
+
if (opts.origin !== "*") {
|
|
1596
1616
|
set("Vary", "Origin");
|
|
1597
1617
|
}
|
|
1598
1618
|
if (opts.maxAge != null) {
|
|
@@ -1622,7 +1642,7 @@ var cors = (options) => {
|
|
|
1622
1642
|
});
|
|
1623
1643
|
}
|
|
1624
1644
|
await next();
|
|
1625
|
-
if (opts.origin !== "*"
|
|
1645
|
+
if (opts.origin !== "*") {
|
|
1626
1646
|
c.header("Vary", "Origin", { append: true });
|
|
1627
1647
|
}
|
|
1628
1648
|
};
|
|
@@ -3199,7 +3219,7 @@ if (process.argv.includes("--version") || process.argv.includes("-V")) {
|
|
|
3199
3219
|
console.log(getPackageVersion());
|
|
3200
3220
|
process.exit(0);
|
|
3201
3221
|
}
|
|
3202
|
-
var PORT = Number(process.env["PORT"] ?? process.env["INSTRUCTIONS_PORT"] ??
|
|
3222
|
+
var PORT = Number(process.env["PORT"] ?? process.env["INSTRUCTIONS_PORT"] ?? 3457);
|
|
3203
3223
|
var app = new Hono2;
|
|
3204
3224
|
app.use("*", cors());
|
|
3205
3225
|
function serviceMode() {
|
|
@@ -3268,7 +3288,7 @@ if (dashDir) {
|
|
|
3268
3288
|
});
|
|
3269
3289
|
});
|
|
3270
3290
|
}
|
|
3271
|
-
var HOST = process.env["HOST"] ?? process.env["INSTRUCTIONS_HOST"] ??
|
|
3291
|
+
var HOST = process.env["HOST"] ?? process.env["INSTRUCTIONS_HOST"] ?? "localhost";
|
|
3272
3292
|
console.log(`instructions-serve listening on http://${HOST}:${PORT} (mode: ${serviceMode()})${dashDir ? " (dashboard: /)" : " (no dashboard)"}`);
|
|
3273
3293
|
var server_default = { port: PORT, hostname: HOST, fetch: app.fetch };
|
|
3274
3294
|
export {
|
package/dist/status.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ConfigStore } from "./data/config-store.js";
|
|
2
|
-
type ActiveDbEnv = "
|
|
2
|
+
type ActiveDbEnv = "HASNA_INSTRUCTIONS_DB_PATH" | null;
|
|
3
3
|
type DatabaseKind = "memory" | "file";
|
|
4
4
|
type ContractStatus = "ok" | "warn";
|
|
5
5
|
export interface ConfigsStatusContract {
|
|
@@ -11,8 +11,7 @@ export interface ConfigsStatusContract {
|
|
|
11
11
|
};
|
|
12
12
|
env: {
|
|
13
13
|
database: {
|
|
14
|
-
primary: "
|
|
15
|
-
fallback: "CONFIGS_DB_PATH";
|
|
14
|
+
primary: "HASNA_INSTRUCTIONS_DB_PATH";
|
|
16
15
|
active: ActiveDbEnv;
|
|
17
16
|
kind: DatabaseKind;
|
|
18
17
|
};
|
package/dist/status.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAQ9E,KAAK,WAAW,GAAG,
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAQ9E,KAAK,WAAW,GAAG,4BAA4B,GAAG,IAAI,CAAC;AACvD,KAAK,YAAY,GAAG,QAAQ,GAAG,MAAM,CAAC;AACtC,KAAK,cAAc,GAAG,IAAI,GAAG,MAAM,CAAC;AAEpC,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,SAAS,CAAC;IACnB,aAAa,EAAE,KAAK,CAAC;IACrB,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,GAAG,EAAE;QACH,QAAQ,EAAE;YACR,OAAO,EAAE,4BAA4B,CAAC;YACtC,MAAM,EAAE,WAAW,CAAC;YACpB,IAAI,EAAE,YAAY,CAAC;SACpB,CAAC;KACH,CAAC;IACF,MAAM,EAAE;QACN,OAAO,EAAE;YACP,KAAK,EAAE,MAAM,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;YACb,SAAS,EAAE,MAAM,CAAC;YAClB,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;QACF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,MAAM,EAAE;QACN,MAAM,EAAE,cAAc,CAAC;QACvB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,wBAAwB,EAAE,MAAM,CAAC;QACjC,QAAQ,EAAE,OAAO,CAAC;QAClB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,oBAAoB,EAAE,OAAO,CAAC;KAC/B,CAAC;IACF,MAAM,EAAE;QACN,oBAAoB,EAAE,KAAK,CAAC;QAC5B,oBAAoB,EAAE,KAAK,CAAC;QAC5B,iBAAiB,EAAE,KAAK,CAAC;QACzB,oBAAoB,EAAE,KAAK,CAAC;QAC5B,0BAA0B,EAAE,IAAI,CAAC;KAClC,CAAC;CACH;AAsBD,wBAAsB,gBAAgB,CACpC,KAAK,GAAE,WAAkC,GACxC,OAAO,CAAC,qBAAqB,CAAC,CAiHhC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/instructions",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
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",
|