@hasna/instructions 0.4.20 → 0.4.21
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 +275 -93
- package/dist/cli/profile-reads.test.d.ts +2 -0
- package/dist/cli/profile-reads.test.d.ts.map +1 -0
- package/dist/data/config-store.d.ts +10 -1
- package/dist/data/config-store.d.ts.map +1 -1
- package/dist/db/profiles.d.ts +4 -1
- package/dist/db/profiles.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +217 -26
- package/dist/lib/bounded-read.d.ts +7 -0
- package/dist/lib/bounded-read.d.ts.map +1 -0
- package/dist/mcp/index.js +299 -126
- package/dist/server/index.js +246 -36
- package/dist/server/openapi.d.ts +246 -20
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/profile-contract.test.d.ts +2 -0
- package/dist/server/profile-contract.test.d.ts.map +1 -0
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage/cloud-store.d.ts +8 -1
- package/dist/storage/cloud-store.d.ts.map +1 -1
- package/dist/types/index.d.ts +24 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -586,6 +586,133 @@ var init_machine = __esm(() => {
|
|
|
586
586
|
init_template();
|
|
587
587
|
});
|
|
588
588
|
|
|
589
|
+
// src/lib/compact-output.ts
|
|
590
|
+
function parseLimit(value, fallback = DEFAULT_LIST_LIMIT, max = MAX_LIST_LIMIT) {
|
|
591
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
|
|
592
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
593
|
+
return fallback;
|
|
594
|
+
return Math.min(Math.floor(parsed), max);
|
|
595
|
+
}
|
|
596
|
+
function parseCursor(value) {
|
|
597
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
|
|
598
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
599
|
+
return 0;
|
|
600
|
+
return Math.floor(parsed);
|
|
601
|
+
}
|
|
602
|
+
function paginate(items, opts = {}) {
|
|
603
|
+
const limit = parseLimit(opts.limit, opts.defaultLimit ?? DEFAULT_LIST_LIMIT, opts.maxLimit ?? MAX_LIST_LIMIT);
|
|
604
|
+
const cursor = parseCursor(opts.cursor);
|
|
605
|
+
const pageItems = items.slice(cursor, cursor + limit);
|
|
606
|
+
const nextCursor = cursor + pageItems.length < items.length ? cursor + pageItems.length : null;
|
|
607
|
+
return {
|
|
608
|
+
items: pageItems,
|
|
609
|
+
total: items.length,
|
|
610
|
+
limit,
|
|
611
|
+
cursor,
|
|
612
|
+
next_cursor: nextCursor,
|
|
613
|
+
has_more: nextCursor !== null
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function pagedPayload(items, opts = {}) {
|
|
617
|
+
return {
|
|
618
|
+
...paginate(items, opts),
|
|
619
|
+
hint: opts.hint
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
function truncateText(value, max = 80) {
|
|
623
|
+
const text = (value ?? "").replace(/\s+/g, " ").trim();
|
|
624
|
+
if (text.length <= max)
|
|
625
|
+
return text;
|
|
626
|
+
if (max <= 3)
|
|
627
|
+
return text.slice(0, max);
|
|
628
|
+
return `${text.slice(0, max - 3)}...`;
|
|
629
|
+
}
|
|
630
|
+
function summarizeConfig(config, opts = {}) {
|
|
631
|
+
const summary = {
|
|
632
|
+
id: config.id,
|
|
633
|
+
slug: config.slug,
|
|
634
|
+
name: config.name,
|
|
635
|
+
category: config.category,
|
|
636
|
+
agent: config.agent,
|
|
637
|
+
kind: config.kind,
|
|
638
|
+
format: config.format,
|
|
639
|
+
target_path: config.target_path,
|
|
640
|
+
output_count: config.outputs.length,
|
|
641
|
+
version: config.version,
|
|
642
|
+
is_template: config.is_template
|
|
643
|
+
};
|
|
644
|
+
if (opts.verbose) {
|
|
645
|
+
summary.updated_at = config.updated_at;
|
|
646
|
+
summary.description = config.description;
|
|
647
|
+
summary.tags = config.tags;
|
|
648
|
+
summary.outputs = config.outputs;
|
|
649
|
+
}
|
|
650
|
+
return summary;
|
|
651
|
+
}
|
|
652
|
+
function summarizeProfile(profile, opts = {}) {
|
|
653
|
+
const selectorCount = (profile.selectors.os?.length ?? 0) + (profile.selectors.arch?.length ?? 0) + (profile.selectors.hostnames?.length ?? 0);
|
|
654
|
+
const summary = {
|
|
655
|
+
id: profile.id,
|
|
656
|
+
slug: profile.slug,
|
|
657
|
+
name: profile.name,
|
|
658
|
+
description: opts.verbose ? profile.description : truncateText(profile.description, 80),
|
|
659
|
+
selector_count: selectorCount,
|
|
660
|
+
variable_count: Object.keys(profile.variables).length
|
|
661
|
+
};
|
|
662
|
+
if (opts.verbose) {
|
|
663
|
+
summary.created_at = profile.created_at;
|
|
664
|
+
summary.updated_at = profile.updated_at;
|
|
665
|
+
summary.selectors = profile.selectors;
|
|
666
|
+
summary.variables = profile.variables;
|
|
667
|
+
}
|
|
668
|
+
return summary;
|
|
669
|
+
}
|
|
670
|
+
function summarizeApplyResult(result) {
|
|
671
|
+
return {
|
|
672
|
+
config_id: result.config_id,
|
|
673
|
+
path: result.path,
|
|
674
|
+
dry_run: result.dry_run,
|
|
675
|
+
changed: result.changed,
|
|
676
|
+
primary_changed: result.primary_changed,
|
|
677
|
+
agent: result.agent,
|
|
678
|
+
transform: result.transform,
|
|
679
|
+
output_count: result.outputs?.length ?? 0,
|
|
680
|
+
outputs: result.outputs?.map(summarizeApplyResult)
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
var DEFAULT_LIST_LIMIT = 20, MAX_LIST_LIMIT = 100;
|
|
684
|
+
|
|
685
|
+
// src/lib/bounded-read.ts
|
|
686
|
+
function normalizeBoundedReadOptions(options = {}) {
|
|
687
|
+
return {
|
|
688
|
+
limit: parseLimit(options.limit, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT),
|
|
689
|
+
cursor: parseCursor(options.cursor)
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function boundedReadPage(items, total, options = {}) {
|
|
693
|
+
const { limit, cursor } = normalizeBoundedReadOptions(options);
|
|
694
|
+
if (items.length > limit) {
|
|
695
|
+
throw new Error(`bounded read returned ${items.length} rows for limit ${limit}`);
|
|
696
|
+
}
|
|
697
|
+
const consumed = cursor + items.length;
|
|
698
|
+
const complete = consumed >= total;
|
|
699
|
+
if (!complete && items.length === 0) {
|
|
700
|
+
throw new Error(`bounded read did not advance at cursor ${cursor} of ${total}`);
|
|
701
|
+
}
|
|
702
|
+
return {
|
|
703
|
+
items,
|
|
704
|
+
total,
|
|
705
|
+
limit,
|
|
706
|
+
cursor,
|
|
707
|
+
next_cursor: complete ? null : consumed,
|
|
708
|
+
has_more: !complete,
|
|
709
|
+
complete,
|
|
710
|
+
truncated: false,
|
|
711
|
+
source_bounded: true
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
var init_bounded_read = () => {};
|
|
715
|
+
|
|
589
716
|
// src/db/profiles.ts
|
|
590
717
|
function rowToProfile(row) {
|
|
591
718
|
return {
|
|
@@ -629,9 +756,12 @@ function getProfile(idOrSlug, db) {
|
|
|
629
756
|
throw new ProfileNotFoundError(idOrSlug);
|
|
630
757
|
return rowToProfile(row);
|
|
631
758
|
}
|
|
632
|
-
function
|
|
759
|
+
function listProfilesPage(options = {}, db) {
|
|
633
760
|
const d = db || getDatabase();
|
|
634
|
-
|
|
761
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
762
|
+
const total = d.query("SELECT COUNT(*) AS total FROM profiles").get()?.total ?? 0;
|
|
763
|
+
const rows = d.query("SELECT * FROM profiles ORDER BY name LIMIT ? OFFSET ?").all(normalized.limit, normalized.cursor).map(rowToProfile);
|
|
764
|
+
return boundedReadPage(rows, total, normalized);
|
|
635
765
|
}
|
|
636
766
|
function updateProfile(idOrSlug, input, db) {
|
|
637
767
|
const d = db || getDatabase();
|
|
@@ -676,14 +806,13 @@ function removeConfigFromProfile(profileIdOrSlug, configId, db) {
|
|
|
676
806
|
const profile = getProfile(profileIdOrSlug, d);
|
|
677
807
|
d.run("DELETE FROM profile_configs WHERE profile_id = ? AND config_id = ?", [profile.id, configId]);
|
|
678
808
|
}
|
|
679
|
-
function
|
|
809
|
+
function getProfileConfigsPage(profileIdOrSlug, options = {}, db) {
|
|
680
810
|
const d = db || getDatabase();
|
|
681
811
|
const profile = getProfile(profileIdOrSlug, d);
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
return listConfigs(undefined, d).filter((c) => ids.includes(c.id));
|
|
812
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
813
|
+
const total = d.query("SELECT COUNT(*) AS total FROM profile_configs WHERE profile_id = ?").get(profile.id)?.total ?? 0;
|
|
814
|
+
const rows = d.query("SELECT config_id FROM profile_configs WHERE profile_id = ? ORDER BY sort_order LIMIT ? OFFSET ?").all(profile.id, normalized.limit, normalized.cursor);
|
|
815
|
+
return boundedReadPage(rows.map((row) => getConfigById(row.config_id, d)), total, normalized);
|
|
687
816
|
}
|
|
688
817
|
function profileHasSelectors(profile) {
|
|
689
818
|
const selectors = profile.selectors ?? {};
|
|
@@ -699,20 +828,46 @@ function profileMatchesMachine(profile, machine) {
|
|
|
699
828
|
const hostnameMatches = !selectors.hostnames?.length || selectors.hostnames.some((candidate) => candidate.trim().toLowerCase() === machine.hostname.trim().toLowerCase());
|
|
700
829
|
return osMatches && archMatches && hostnameMatches;
|
|
701
830
|
}
|
|
702
|
-
function
|
|
703
|
-
const
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
831
|
+
function resolveProfileForMachineRead(machine = detectMachineContext(), options = {}, db) {
|
|
832
|
+
const d = db || getDatabase();
|
|
833
|
+
const { limit } = normalizeBoundedReadOptions(options);
|
|
834
|
+
let cursor = 0;
|
|
835
|
+
let scanned = 0;
|
|
836
|
+
let total = 0;
|
|
837
|
+
let selected = null;
|
|
838
|
+
while (true) {
|
|
839
|
+
const page = listProfilesPage({ limit, cursor }, d);
|
|
840
|
+
total = page.total;
|
|
841
|
+
scanned += page.items.length;
|
|
842
|
+
for (const profile of page.items) {
|
|
843
|
+
if (!profileHasSelectors(profile) || !profileMatchesMachine(profile, machine))
|
|
844
|
+
continue;
|
|
845
|
+
const selectors = profile.selectors;
|
|
846
|
+
const score = (selectors.hostnames?.length ? 100 : 0) + (selectors.os?.length ? 10 : 0) + (selectors.arch?.length ? 10 : 0);
|
|
847
|
+
if (!selected || score > selected.score || score === selected.score && profile.name.localeCompare(selected.profile.name) < 0) {
|
|
848
|
+
selected = { profile, score };
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
if (page.complete)
|
|
852
|
+
break;
|
|
853
|
+
cursor = page.next_cursor;
|
|
854
|
+
}
|
|
855
|
+
return {
|
|
856
|
+
profile: selected?.profile ?? null,
|
|
857
|
+
scanned,
|
|
858
|
+
total,
|
|
859
|
+
batch_limit: limit,
|
|
860
|
+
source_bounded: true,
|
|
861
|
+
complete: true,
|
|
862
|
+
truncated: false
|
|
863
|
+
};
|
|
710
864
|
}
|
|
711
865
|
var init_profiles = __esm(() => {
|
|
712
866
|
init_types();
|
|
713
867
|
init_database();
|
|
714
868
|
init_configs();
|
|
715
869
|
init_machine();
|
|
870
|
+
init_bounded_read();
|
|
716
871
|
});
|
|
717
872
|
|
|
718
873
|
// src/db/snapshots.ts
|
|
@@ -790,6 +945,32 @@ var init_machines = __esm(() => {
|
|
|
790
945
|
|
|
791
946
|
// src/data/config-store.ts
|
|
792
947
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
948
|
+
function parseBoundedPagePayload(value, label) {
|
|
949
|
+
const page = value;
|
|
950
|
+
const consumed = Number(page?.cursor) + (page?.items?.length ?? 0);
|
|
951
|
+
const complete = Boolean(page && Number.isSafeInteger(page.total) && consumed >= Number(page.total));
|
|
952
|
+
if (!page || !Array.isArray(page.items) || !Number.isSafeInteger(page.total) || Number(page.total) < 0 || !Number.isSafeInteger(page.limit) || Number(page.limit) < 1 || !Number.isSafeInteger(page.cursor) || Number(page.cursor) < 0 || page.items.length > Number(page.limit) || typeof page.has_more !== "boolean" || typeof page.complete !== "boolean" || page.truncated !== false || page.next_cursor !== null && !Number.isSafeInteger(page.next_cursor) || page.complete !== complete || page.has_more !== !complete || page.next_cursor !== (complete ? null : consumed)) {
|
|
953
|
+
throw new CloudHttpError(502, `${label} returned an invalid or truncated bounded-read envelope`, value);
|
|
954
|
+
}
|
|
955
|
+
return {
|
|
956
|
+
...page,
|
|
957
|
+
source_bounded: page.source_bounded ?? true
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
function parseBoundedOrLegacyPage(value, legacyItems, options, label) {
|
|
961
|
+
if (value && typeof value === "object") {
|
|
962
|
+
const candidate = value;
|
|
963
|
+
if ("items" in candidate || "total" in candidate || "complete" in candidate || "truncated" in candidate || "next_cursor" in candidate) {
|
|
964
|
+
return parseBoundedPagePayload(value, label);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
if (!Array.isArray(legacyItems)) {
|
|
968
|
+
throw new CloudHttpError(502, `${label} returned neither a bounded envelope nor a complete legacy array`, value);
|
|
969
|
+
}
|
|
970
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
971
|
+
const page = boundedReadPage(legacyItems.slice(normalized.cursor, normalized.cursor + normalized.limit), legacyItems.length, normalized);
|
|
972
|
+
return { ...page, source_bounded: false };
|
|
973
|
+
}
|
|
793
974
|
function resolveCloudConfig(env = process.env) {
|
|
794
975
|
const apiUrl = env[API_URL_ENV]?.trim();
|
|
795
976
|
const apiKey = env[API_KEY_ENV]?.trim();
|
|
@@ -844,13 +1025,35 @@ class LocalConfigStore {
|
|
|
844
1025
|
return pruneSnapshots(configId, keep, this.db);
|
|
845
1026
|
}
|
|
846
1027
|
async listProfiles() {
|
|
847
|
-
|
|
1028
|
+
const profiles = [];
|
|
1029
|
+
let cursor = 0;
|
|
1030
|
+
while (true) {
|
|
1031
|
+
const page = await this.listProfilesPage({ limit: 100, cursor });
|
|
1032
|
+
profiles.push(...page.items);
|
|
1033
|
+
if (page.complete)
|
|
1034
|
+
return profiles;
|
|
1035
|
+
cursor = page.next_cursor;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
async listProfilesPage(options = {}) {
|
|
1039
|
+
return listProfilesPage(options, this.db);
|
|
848
1040
|
}
|
|
849
1041
|
async getProfile(idOrSlug) {
|
|
850
1042
|
return getProfile(idOrSlug, this.db);
|
|
851
1043
|
}
|
|
852
1044
|
async getProfileConfigs(idOrSlug) {
|
|
853
|
-
|
|
1045
|
+
const configs = [];
|
|
1046
|
+
let cursor = 0;
|
|
1047
|
+
while (true) {
|
|
1048
|
+
const page = await this.getProfileConfigsPage(idOrSlug, { limit: 100, cursor });
|
|
1049
|
+
configs.push(...page.items);
|
|
1050
|
+
if (page.complete)
|
|
1051
|
+
return configs;
|
|
1052
|
+
cursor = page.next_cursor;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
async getProfileConfigsPage(idOrSlug, options = {}) {
|
|
1056
|
+
return getProfileConfigsPage(idOrSlug, options, this.db);
|
|
854
1057
|
}
|
|
855
1058
|
async createProfile(input) {
|
|
856
1059
|
return createProfile(input, this.db);
|
|
@@ -868,7 +1071,10 @@ class LocalConfigStore {
|
|
|
868
1071
|
removeConfigFromProfile(profileIdOrSlug, configId, this.db);
|
|
869
1072
|
}
|
|
870
1073
|
async resolveProfileForMachine(machine) {
|
|
871
|
-
return
|
|
1074
|
+
return (await this.resolveProfileForMachineRead(machine)).profile;
|
|
1075
|
+
}
|
|
1076
|
+
async resolveProfileForMachineRead(machine, options = {}) {
|
|
1077
|
+
return machine ? resolveProfileForMachineRead(machine, options, this.db) : resolveProfileForMachineRead(undefined, options, this.db);
|
|
872
1078
|
}
|
|
873
1079
|
async registerMachine(hostname2, os, arch2) {
|
|
874
1080
|
return registerMachine(hostname2, os, arch2, this.db);
|
|
@@ -1009,8 +1215,24 @@ class CloudConfigStore {
|
|
|
1009
1215
|
return data?.pruned ?? 0;
|
|
1010
1216
|
}
|
|
1011
1217
|
async listProfiles() {
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1218
|
+
const profiles = [];
|
|
1219
|
+
let cursor = 0;
|
|
1220
|
+
while (true) {
|
|
1221
|
+
const page = await this.listProfilesPage({ limit: 100, cursor });
|
|
1222
|
+
profiles.push(...page.items);
|
|
1223
|
+
if (page.complete)
|
|
1224
|
+
return profiles;
|
|
1225
|
+
cursor = page.next_cursor;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
async listProfilesPage(options = {}) {
|
|
1229
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
1230
|
+
const params = new URLSearchParams;
|
|
1231
|
+
params.set("limit", String(normalized.limit));
|
|
1232
|
+
params.set("cursor", String(normalized.cursor));
|
|
1233
|
+
const qs = params.toString();
|
|
1234
|
+
const { data } = await this.request("GET", `/profiles${qs ? `?${qs}` : ""}`);
|
|
1235
|
+
return parseBoundedOrLegacyPage(data, data?.profiles, normalized, "profile list");
|
|
1014
1236
|
}
|
|
1015
1237
|
async getProfile(idOrSlug) {
|
|
1016
1238
|
const { status, data } = await this.request("GET", `/profiles/${encodeURIComponent(idOrSlug)}`, undefined, { allow404: true });
|
|
@@ -1020,10 +1242,26 @@ class CloudConfigStore {
|
|
|
1020
1242
|
return profile;
|
|
1021
1243
|
}
|
|
1022
1244
|
async getProfileConfigs(idOrSlug) {
|
|
1023
|
-
const
|
|
1245
|
+
const configs = [];
|
|
1246
|
+
let cursor = 0;
|
|
1247
|
+
while (true) {
|
|
1248
|
+
const page = await this.getProfileConfigsPage(idOrSlug, { limit: 100, cursor });
|
|
1249
|
+
configs.push(...page.items);
|
|
1250
|
+
if (page.complete)
|
|
1251
|
+
return configs;
|
|
1252
|
+
cursor = page.next_cursor;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
async getProfileConfigsPage(idOrSlug, options = {}) {
|
|
1256
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
1257
|
+
const params = new URLSearchParams;
|
|
1258
|
+
params.set("limit", String(normalized.limit));
|
|
1259
|
+
params.set("cursor", String(normalized.cursor));
|
|
1260
|
+
const qs = params.toString();
|
|
1261
|
+
const { status, data } = await this.request("GET", `/profiles/${encodeURIComponent(idOrSlug)}${qs ? `?${qs}` : ""}`, undefined, { allow404: true });
|
|
1024
1262
|
if (status === 404 || !data?.profile)
|
|
1025
1263
|
throw new ProfileNotFoundError(idOrSlug);
|
|
1026
|
-
return data.profile.configs
|
|
1264
|
+
return parseBoundedOrLegacyPage(data.configs, data.profile.configs, normalized, "profile membership");
|
|
1027
1265
|
}
|
|
1028
1266
|
async createProfile(input) {
|
|
1029
1267
|
const { data } = await this.request("POST", "/profiles", input, {
|
|
@@ -1047,6 +1285,10 @@ class CloudConfigStore {
|
|
|
1047
1285
|
await this.request("DELETE", `/profiles/${encodeURIComponent(profileIdOrSlug)}/configs/${encodeURIComponent(configId)}`, undefined, { allow404: true });
|
|
1048
1286
|
}
|
|
1049
1287
|
async resolveProfileForMachine(machine) {
|
|
1288
|
+
return (await this.resolveProfileForMachineRead(machine)).profile;
|
|
1289
|
+
}
|
|
1290
|
+
async resolveProfileForMachineRead(machine, options = {}) {
|
|
1291
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
1050
1292
|
const params = new URLSearchParams;
|
|
1051
1293
|
if (machine?.hostname)
|
|
1052
1294
|
params.set("hostname", machine.hostname);
|
|
@@ -1054,11 +1296,40 @@ class CloudConfigStore {
|
|
|
1054
1296
|
params.set("os", machine.os);
|
|
1055
1297
|
if (machine?.arch)
|
|
1056
1298
|
params.set("arch", machine.arch);
|
|
1299
|
+
params.set("limit", String(normalized.limit));
|
|
1057
1300
|
const qs = params.toString();
|
|
1058
1301
|
const { status, data } = await this.request("GET", `/profiles/resolve${qs ? `?${qs}` : ""}`, undefined, { allow404: true });
|
|
1059
|
-
if (status === 404
|
|
1060
|
-
return
|
|
1061
|
-
|
|
1302
|
+
if (status === 404) {
|
|
1303
|
+
return {
|
|
1304
|
+
profile: null,
|
|
1305
|
+
scanned: null,
|
|
1306
|
+
total: null,
|
|
1307
|
+
batch_limit: null,
|
|
1308
|
+
source_bounded: false,
|
|
1309
|
+
complete: true,
|
|
1310
|
+
truncated: false
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
if (data && "complete" in data) {
|
|
1314
|
+
if (data.complete !== true || data.truncated !== false) {
|
|
1315
|
+
throw new CloudHttpError(502, "profile resolve returned an incomplete or truncated read", data);
|
|
1316
|
+
}
|
|
1317
|
+
return { ...data, source_bounded: data.source_bounded ?? true };
|
|
1318
|
+
}
|
|
1319
|
+
if (data && "profile" in data) {
|
|
1320
|
+
return {
|
|
1321
|
+
profile: data.profile,
|
|
1322
|
+
scanned: null,
|
|
1323
|
+
total: null,
|
|
1324
|
+
batch_limit: null,
|
|
1325
|
+
source_bounded: false,
|
|
1326
|
+
complete: true,
|
|
1327
|
+
truncated: false
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
{
|
|
1331
|
+
throw new CloudHttpError(502, "profile resolve returned an incomplete or truncated read", data);
|
|
1332
|
+
}
|
|
1062
1333
|
}
|
|
1063
1334
|
async registerMachine(hostname2, os, arch2) {
|
|
1064
1335
|
const { data } = await this.request("POST", "/machines", { hostname: hostname2, os, arch: arch2 }, { idempotent: true });
|
|
@@ -1095,6 +1366,7 @@ var init_config_store = __esm(() => {
|
|
|
1095
1366
|
init_machines();
|
|
1096
1367
|
init_database();
|
|
1097
1368
|
init_types();
|
|
1369
|
+
init_bounded_read();
|
|
1098
1370
|
CloudHttpError = class CloudHttpError extends Error {
|
|
1099
1371
|
status;
|
|
1100
1372
|
body;
|
|
@@ -6957,7 +7229,7 @@ var init_sync_dir = __esm(() => {
|
|
|
6957
7229
|
var require_package = __commonJS((exports, module) => {
|
|
6958
7230
|
module.exports = {
|
|
6959
7231
|
name: "@hasna/instructions",
|
|
6960
|
-
version: "0.4.
|
|
7232
|
+
version: "0.4.21",
|
|
6961
7233
|
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.",
|
|
6962
7234
|
type: "module",
|
|
6963
7235
|
main: "dist/index.js",
|
|
@@ -7081,105 +7353,6 @@ function findReferenceConfigsByName(configs, name) {
|
|
|
7081
7353
|
// src/mcp/server.ts
|
|
7082
7354
|
init_sync_dir();
|
|
7083
7355
|
init_machine();
|
|
7084
|
-
|
|
7085
|
-
// src/lib/compact-output.ts
|
|
7086
|
-
var DEFAULT_LIST_LIMIT = 20;
|
|
7087
|
-
var MAX_LIST_LIMIT = 100;
|
|
7088
|
-
function parseLimit(value, fallback = DEFAULT_LIST_LIMIT, max = MAX_LIST_LIMIT) {
|
|
7089
|
-
const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
|
|
7090
|
-
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
7091
|
-
return fallback;
|
|
7092
|
-
return Math.min(Math.floor(parsed), max);
|
|
7093
|
-
}
|
|
7094
|
-
function parseCursor(value) {
|
|
7095
|
-
const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
|
|
7096
|
-
if (!Number.isFinite(parsed) || parsed < 0)
|
|
7097
|
-
return 0;
|
|
7098
|
-
return Math.floor(parsed);
|
|
7099
|
-
}
|
|
7100
|
-
function paginate(items, opts = {}) {
|
|
7101
|
-
const limit = parseLimit(opts.limit, opts.defaultLimit ?? DEFAULT_LIST_LIMIT, opts.maxLimit ?? MAX_LIST_LIMIT);
|
|
7102
|
-
const cursor = parseCursor(opts.cursor);
|
|
7103
|
-
const pageItems = items.slice(cursor, cursor + limit);
|
|
7104
|
-
const nextCursor = cursor + pageItems.length < items.length ? cursor + pageItems.length : null;
|
|
7105
|
-
return {
|
|
7106
|
-
items: pageItems,
|
|
7107
|
-
total: items.length,
|
|
7108
|
-
limit,
|
|
7109
|
-
cursor,
|
|
7110
|
-
next_cursor: nextCursor,
|
|
7111
|
-
has_more: nextCursor !== null
|
|
7112
|
-
};
|
|
7113
|
-
}
|
|
7114
|
-
function pagedPayload(items, opts = {}) {
|
|
7115
|
-
return {
|
|
7116
|
-
...paginate(items, opts),
|
|
7117
|
-
hint: opts.hint
|
|
7118
|
-
};
|
|
7119
|
-
}
|
|
7120
|
-
function truncateText(value, max = 80) {
|
|
7121
|
-
const text = (value ?? "").replace(/\s+/g, " ").trim();
|
|
7122
|
-
if (text.length <= max)
|
|
7123
|
-
return text;
|
|
7124
|
-
if (max <= 3)
|
|
7125
|
-
return text.slice(0, max);
|
|
7126
|
-
return `${text.slice(0, max - 3)}...`;
|
|
7127
|
-
}
|
|
7128
|
-
function summarizeConfig(config, opts = {}) {
|
|
7129
|
-
const summary = {
|
|
7130
|
-
id: config.id,
|
|
7131
|
-
slug: config.slug,
|
|
7132
|
-
name: config.name,
|
|
7133
|
-
category: config.category,
|
|
7134
|
-
agent: config.agent,
|
|
7135
|
-
kind: config.kind,
|
|
7136
|
-
format: config.format,
|
|
7137
|
-
target_path: config.target_path,
|
|
7138
|
-
output_count: config.outputs.length,
|
|
7139
|
-
version: config.version,
|
|
7140
|
-
is_template: config.is_template
|
|
7141
|
-
};
|
|
7142
|
-
if (opts.verbose) {
|
|
7143
|
-
summary.updated_at = config.updated_at;
|
|
7144
|
-
summary.description = config.description;
|
|
7145
|
-
summary.tags = config.tags;
|
|
7146
|
-
summary.outputs = config.outputs;
|
|
7147
|
-
}
|
|
7148
|
-
return summary;
|
|
7149
|
-
}
|
|
7150
|
-
function summarizeProfile(profile, opts = {}) {
|
|
7151
|
-
const selectorCount = (profile.selectors.os?.length ?? 0) + (profile.selectors.arch?.length ?? 0) + (profile.selectors.hostnames?.length ?? 0);
|
|
7152
|
-
const summary = {
|
|
7153
|
-
id: profile.id,
|
|
7154
|
-
slug: profile.slug,
|
|
7155
|
-
name: profile.name,
|
|
7156
|
-
description: opts.verbose ? profile.description : truncateText(profile.description, 80),
|
|
7157
|
-
selector_count: selectorCount,
|
|
7158
|
-
variable_count: Object.keys(profile.variables).length
|
|
7159
|
-
};
|
|
7160
|
-
if (opts.verbose) {
|
|
7161
|
-
summary.created_at = profile.created_at;
|
|
7162
|
-
summary.updated_at = profile.updated_at;
|
|
7163
|
-
summary.selectors = profile.selectors;
|
|
7164
|
-
summary.variables = profile.variables;
|
|
7165
|
-
}
|
|
7166
|
-
return summary;
|
|
7167
|
-
}
|
|
7168
|
-
function summarizeApplyResult(result) {
|
|
7169
|
-
return {
|
|
7170
|
-
config_id: result.config_id,
|
|
7171
|
-
path: result.path,
|
|
7172
|
-
dry_run: result.dry_run,
|
|
7173
|
-
changed: result.changed,
|
|
7174
|
-
primary_changed: result.primary_changed,
|
|
7175
|
-
agent: result.agent,
|
|
7176
|
-
transform: result.transform,
|
|
7177
|
-
output_count: result.outputs?.length ?? 0,
|
|
7178
|
-
outputs: result.outputs?.map(summarizeApplyResult)
|
|
7179
|
-
};
|
|
7180
|
-
}
|
|
7181
|
-
|
|
7182
|
-
// src/mcp/server.ts
|
|
7183
7356
|
var TOOL_DOCS = {
|
|
7184
7357
|
list_configs: "List configs. Params: category?, agent?, kind?, search?, limit?, cursor?, verbose?. Defaults to a paged compact envelope without content; use get_config for full content.",
|
|
7185
7358
|
get_config: "Get a config by id or slug. Returns full config including content.",
|