@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../src/db/profiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EACV,MAAM,EACN,kBAAkB,EAClB,OAAO,EAIP,kBAAkB,EAClB,cAAc,EACf,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"profiles.d.ts","sourceRoot":"","sources":["../../src/db/profiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EACV,MAAM,EACN,kBAAkB,EAClB,OAAO,EAIP,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AA4B3B,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAmB/E;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CASnE;AAED,wBAAgB,YAAY,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,EAAE,CAMrD;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,kBAAuB,EAChC,EAAE,CAAC,EAAE,QAAQ,GACZ,eAAe,CAAC,OAAO,CAAC,CAS1B;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,kBAAkB,EACzB,EAAE,CAAC,EAAE,QAAQ,GACZ,OAAO,CA0BT;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAInE;AAED,wBAAgB,kBAAkB,CAChC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,IAAI,CAaN;AAED,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,IAAI,CAON;AAED,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,EAAE,CAUlF;AAED,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,MAAM,EACvB,OAAO,GAAE,kBAAuB,EAChC,EAAE,CAAC,EAAE,QAAQ,GACZ,eAAe,CAAC,MAAM,CAAC,CAazB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,GAAG,OAAO,CAKhF;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EACnC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,IAAI,GAAG,MAAM,GAAG,WAAW,CAAC,GACtE,OAAO,CAYT;AAED,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,cAAuC,EAChD,EAAE,CAAC,EAAE,QAAQ,GACZ,OAAO,GAAG,IAAI,CAEhB;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,GAAE,cAAuC,EAChD,OAAO,GAAE,kBAAuB,EAChC,EAAE,CAAC,EAAE,QAAQ,GACZ,qBAAqB,CAwCvB"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from "./types/index.js";
|
|
2
2
|
export { CloudConfigStore, CloudHttpError, LocalConfigStore, isCloudMode, resolveCloudConfig, resolveConfigStore, } from "./data/config-store.js";
|
|
3
3
|
export type { CloudConfig, ConfigStore } from "./data/config-store.js";
|
|
4
|
+
export { boundedReadPage, normalizeBoundedReadOptions } from "./lib/bounded-read.js";
|
|
4
5
|
export { currentHostname, currentOs, currentArch } from "./db/machines.js";
|
|
5
6
|
export { uuid, now, slugify } from "./db/database.js";
|
|
6
7
|
export { getConfigsStatus } from "./status.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAC;AAIjC,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -596,6 +596,52 @@ function renderMachineAwareContentPreview(content, variables) {
|
|
|
596
596
|
return isTemplate(content) ? renderTemplatePreview(content, variables) : { content, unresolved: [] };
|
|
597
597
|
}
|
|
598
598
|
|
|
599
|
+
// src/lib/compact-output.ts
|
|
600
|
+
var DEFAULT_LIST_LIMIT = 20;
|
|
601
|
+
var MAX_LIST_LIMIT = 100;
|
|
602
|
+
function parseLimit(value, fallback = DEFAULT_LIST_LIMIT, max = MAX_LIST_LIMIT) {
|
|
603
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
|
|
604
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
605
|
+
return fallback;
|
|
606
|
+
return Math.min(Math.floor(parsed), max);
|
|
607
|
+
}
|
|
608
|
+
function parseCursor(value) {
|
|
609
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
|
|
610
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
611
|
+
return 0;
|
|
612
|
+
return Math.floor(parsed);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// src/lib/bounded-read.ts
|
|
616
|
+
function normalizeBoundedReadOptions(options = {}) {
|
|
617
|
+
return {
|
|
618
|
+
limit: parseLimit(options.limit, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT),
|
|
619
|
+
cursor: parseCursor(options.cursor)
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
function boundedReadPage(items, total, options = {}) {
|
|
623
|
+
const { limit, cursor } = normalizeBoundedReadOptions(options);
|
|
624
|
+
if (items.length > limit) {
|
|
625
|
+
throw new Error(`bounded read returned ${items.length} rows for limit ${limit}`);
|
|
626
|
+
}
|
|
627
|
+
const consumed = cursor + items.length;
|
|
628
|
+
const complete = consumed >= total;
|
|
629
|
+
if (!complete && items.length === 0) {
|
|
630
|
+
throw new Error(`bounded read did not advance at cursor ${cursor} of ${total}`);
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
items,
|
|
634
|
+
total,
|
|
635
|
+
limit,
|
|
636
|
+
cursor,
|
|
637
|
+
next_cursor: complete ? null : consumed,
|
|
638
|
+
has_more: !complete,
|
|
639
|
+
complete,
|
|
640
|
+
truncated: false,
|
|
641
|
+
source_bounded: true
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
599
645
|
// src/db/profiles.ts
|
|
600
646
|
function rowToProfile(row) {
|
|
601
647
|
return {
|
|
@@ -639,9 +685,12 @@ function getProfile(idOrSlug, db) {
|
|
|
639
685
|
throw new ProfileNotFoundError(idOrSlug);
|
|
640
686
|
return rowToProfile(row);
|
|
641
687
|
}
|
|
642
|
-
function
|
|
688
|
+
function listProfilesPage(options = {}, db) {
|
|
643
689
|
const d = db || getDatabase();
|
|
644
|
-
|
|
690
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
691
|
+
const total = d.query("SELECT COUNT(*) AS total FROM profiles").get()?.total ?? 0;
|
|
692
|
+
const rows = d.query("SELECT * FROM profiles ORDER BY name LIMIT ? OFFSET ?").all(normalized.limit, normalized.cursor).map(rowToProfile);
|
|
693
|
+
return boundedReadPage(rows, total, normalized);
|
|
645
694
|
}
|
|
646
695
|
function updateProfile(idOrSlug, input, db) {
|
|
647
696
|
const d = db || getDatabase();
|
|
@@ -686,14 +735,13 @@ function removeConfigFromProfile(profileIdOrSlug, configId, db) {
|
|
|
686
735
|
const profile = getProfile(profileIdOrSlug, d);
|
|
687
736
|
d.run("DELETE FROM profile_configs WHERE profile_id = ? AND config_id = ?", [profile.id, configId]);
|
|
688
737
|
}
|
|
689
|
-
function
|
|
738
|
+
function getProfileConfigsPage(profileIdOrSlug, options = {}, db) {
|
|
690
739
|
const d = db || getDatabase();
|
|
691
740
|
const profile = getProfile(profileIdOrSlug, d);
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
return listConfigs(undefined, d).filter((c) => ids.includes(c.id));
|
|
741
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
742
|
+
const total = d.query("SELECT COUNT(*) AS total FROM profile_configs WHERE profile_id = ?").get(profile.id)?.total ?? 0;
|
|
743
|
+
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);
|
|
744
|
+
return boundedReadPage(rows.map((row) => getConfigById(row.config_id, d)), total, normalized);
|
|
697
745
|
}
|
|
698
746
|
function profileHasSelectors(profile) {
|
|
699
747
|
const selectors = profile.selectors ?? {};
|
|
@@ -709,14 +757,39 @@ function profileMatchesMachine(profile, machine) {
|
|
|
709
757
|
const hostnameMatches = !selectors.hostnames?.length || selectors.hostnames.some((candidate) => candidate.trim().toLowerCase() === machine.hostname.trim().toLowerCase());
|
|
710
758
|
return osMatches && archMatches && hostnameMatches;
|
|
711
759
|
}
|
|
712
|
-
function
|
|
713
|
-
const
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
760
|
+
function resolveProfileForMachineRead(machine = detectMachineContext(), options = {}, db) {
|
|
761
|
+
const d = db || getDatabase();
|
|
762
|
+
const { limit } = normalizeBoundedReadOptions(options);
|
|
763
|
+
let cursor = 0;
|
|
764
|
+
let scanned = 0;
|
|
765
|
+
let total = 0;
|
|
766
|
+
let selected = null;
|
|
767
|
+
while (true) {
|
|
768
|
+
const page = listProfilesPage({ limit, cursor }, d);
|
|
769
|
+
total = page.total;
|
|
770
|
+
scanned += page.items.length;
|
|
771
|
+
for (const profile of page.items) {
|
|
772
|
+
if (!profileHasSelectors(profile) || !profileMatchesMachine(profile, machine))
|
|
773
|
+
continue;
|
|
774
|
+
const selectors = profile.selectors;
|
|
775
|
+
const score = (selectors.hostnames?.length ? 100 : 0) + (selectors.os?.length ? 10 : 0) + (selectors.arch?.length ? 10 : 0);
|
|
776
|
+
if (!selected || score > selected.score || score === selected.score && profile.name.localeCompare(selected.profile.name) < 0) {
|
|
777
|
+
selected = { profile, score };
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
if (page.complete)
|
|
781
|
+
break;
|
|
782
|
+
cursor = page.next_cursor;
|
|
783
|
+
}
|
|
784
|
+
return {
|
|
785
|
+
profile: selected?.profile ?? null,
|
|
786
|
+
scanned,
|
|
787
|
+
total,
|
|
788
|
+
batch_limit: limit,
|
|
789
|
+
source_bounded: true,
|
|
790
|
+
complete: true,
|
|
791
|
+
truncated: false
|
|
792
|
+
};
|
|
720
793
|
}
|
|
721
794
|
|
|
722
795
|
// src/db/snapshots.ts
|
|
@@ -797,6 +870,32 @@ class CloudHttpError extends Error {
|
|
|
797
870
|
this.name = "CloudHttpError";
|
|
798
871
|
}
|
|
799
872
|
}
|
|
873
|
+
function parseBoundedPagePayload(value, label) {
|
|
874
|
+
const page = value;
|
|
875
|
+
const consumed = Number(page?.cursor) + (page?.items?.length ?? 0);
|
|
876
|
+
const complete = Boolean(page && Number.isSafeInteger(page.total) && consumed >= Number(page.total));
|
|
877
|
+
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)) {
|
|
878
|
+
throw new CloudHttpError(502, `${label} returned an invalid or truncated bounded-read envelope`, value);
|
|
879
|
+
}
|
|
880
|
+
return {
|
|
881
|
+
...page,
|
|
882
|
+
source_bounded: page.source_bounded ?? true
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
function parseBoundedOrLegacyPage(value, legacyItems, options, label) {
|
|
886
|
+
if (value && typeof value === "object") {
|
|
887
|
+
const candidate = value;
|
|
888
|
+
if ("items" in candidate || "total" in candidate || "complete" in candidate || "truncated" in candidate || "next_cursor" in candidate) {
|
|
889
|
+
return parseBoundedPagePayload(value, label);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
if (!Array.isArray(legacyItems)) {
|
|
893
|
+
throw new CloudHttpError(502, `${label} returned neither a bounded envelope nor a complete legacy array`, value);
|
|
894
|
+
}
|
|
895
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
896
|
+
const page = boundedReadPage(legacyItems.slice(normalized.cursor, normalized.cursor + normalized.limit), legacyItems.length, normalized);
|
|
897
|
+
return { ...page, source_bounded: false };
|
|
898
|
+
}
|
|
800
899
|
var API_URL_ENV = "HASNA_INSTRUCTIONS_API_URL";
|
|
801
900
|
var API_KEY_ENV = "HASNA_INSTRUCTIONS_API_KEY";
|
|
802
901
|
function resolveCloudConfig(env = process.env) {
|
|
@@ -856,13 +955,35 @@ class LocalConfigStore {
|
|
|
856
955
|
return pruneSnapshots(configId, keep, this.db);
|
|
857
956
|
}
|
|
858
957
|
async listProfiles() {
|
|
859
|
-
|
|
958
|
+
const profiles = [];
|
|
959
|
+
let cursor = 0;
|
|
960
|
+
while (true) {
|
|
961
|
+
const page = await this.listProfilesPage({ limit: 100, cursor });
|
|
962
|
+
profiles.push(...page.items);
|
|
963
|
+
if (page.complete)
|
|
964
|
+
return profiles;
|
|
965
|
+
cursor = page.next_cursor;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
async listProfilesPage(options = {}) {
|
|
969
|
+
return listProfilesPage(options, this.db);
|
|
860
970
|
}
|
|
861
971
|
async getProfile(idOrSlug) {
|
|
862
972
|
return getProfile(idOrSlug, this.db);
|
|
863
973
|
}
|
|
864
974
|
async getProfileConfigs(idOrSlug) {
|
|
865
|
-
|
|
975
|
+
const configs = [];
|
|
976
|
+
let cursor = 0;
|
|
977
|
+
while (true) {
|
|
978
|
+
const page = await this.getProfileConfigsPage(idOrSlug, { limit: 100, cursor });
|
|
979
|
+
configs.push(...page.items);
|
|
980
|
+
if (page.complete)
|
|
981
|
+
return configs;
|
|
982
|
+
cursor = page.next_cursor;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
async getProfileConfigsPage(idOrSlug, options = {}) {
|
|
986
|
+
return getProfileConfigsPage(idOrSlug, options, this.db);
|
|
866
987
|
}
|
|
867
988
|
async createProfile(input) {
|
|
868
989
|
return createProfile(input, this.db);
|
|
@@ -880,7 +1001,10 @@ class LocalConfigStore {
|
|
|
880
1001
|
removeConfigFromProfile(profileIdOrSlug, configId, this.db);
|
|
881
1002
|
}
|
|
882
1003
|
async resolveProfileForMachine(machine) {
|
|
883
|
-
return
|
|
1004
|
+
return (await this.resolveProfileForMachineRead(machine)).profile;
|
|
1005
|
+
}
|
|
1006
|
+
async resolveProfileForMachineRead(machine, options = {}) {
|
|
1007
|
+
return machine ? resolveProfileForMachineRead(machine, options, this.db) : resolveProfileForMachineRead(undefined, options, this.db);
|
|
884
1008
|
}
|
|
885
1009
|
async registerMachine(hostname2, os, arch2) {
|
|
886
1010
|
return registerMachine(hostname2, os, arch2, this.db);
|
|
@@ -1021,8 +1145,24 @@ class CloudConfigStore {
|
|
|
1021
1145
|
return data?.pruned ?? 0;
|
|
1022
1146
|
}
|
|
1023
1147
|
async listProfiles() {
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1148
|
+
const profiles = [];
|
|
1149
|
+
let cursor = 0;
|
|
1150
|
+
while (true) {
|
|
1151
|
+
const page = await this.listProfilesPage({ limit: 100, cursor });
|
|
1152
|
+
profiles.push(...page.items);
|
|
1153
|
+
if (page.complete)
|
|
1154
|
+
return profiles;
|
|
1155
|
+
cursor = page.next_cursor;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
async listProfilesPage(options = {}) {
|
|
1159
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
1160
|
+
const params = new URLSearchParams;
|
|
1161
|
+
params.set("limit", String(normalized.limit));
|
|
1162
|
+
params.set("cursor", String(normalized.cursor));
|
|
1163
|
+
const qs = params.toString();
|
|
1164
|
+
const { data } = await this.request("GET", `/profiles${qs ? `?${qs}` : ""}`);
|
|
1165
|
+
return parseBoundedOrLegacyPage(data, data?.profiles, normalized, "profile list");
|
|
1026
1166
|
}
|
|
1027
1167
|
async getProfile(idOrSlug) {
|
|
1028
1168
|
const { status, data } = await this.request("GET", `/profiles/${encodeURIComponent(idOrSlug)}`, undefined, { allow404: true });
|
|
@@ -1032,10 +1172,26 @@ class CloudConfigStore {
|
|
|
1032
1172
|
return profile;
|
|
1033
1173
|
}
|
|
1034
1174
|
async getProfileConfigs(idOrSlug) {
|
|
1035
|
-
const
|
|
1175
|
+
const configs = [];
|
|
1176
|
+
let cursor = 0;
|
|
1177
|
+
while (true) {
|
|
1178
|
+
const page = await this.getProfileConfigsPage(idOrSlug, { limit: 100, cursor });
|
|
1179
|
+
configs.push(...page.items);
|
|
1180
|
+
if (page.complete)
|
|
1181
|
+
return configs;
|
|
1182
|
+
cursor = page.next_cursor;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
async getProfileConfigsPage(idOrSlug, options = {}) {
|
|
1186
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
1187
|
+
const params = new URLSearchParams;
|
|
1188
|
+
params.set("limit", String(normalized.limit));
|
|
1189
|
+
params.set("cursor", String(normalized.cursor));
|
|
1190
|
+
const qs = params.toString();
|
|
1191
|
+
const { status, data } = await this.request("GET", `/profiles/${encodeURIComponent(idOrSlug)}${qs ? `?${qs}` : ""}`, undefined, { allow404: true });
|
|
1036
1192
|
if (status === 404 || !data?.profile)
|
|
1037
1193
|
throw new ProfileNotFoundError(idOrSlug);
|
|
1038
|
-
return data.profile.configs
|
|
1194
|
+
return parseBoundedOrLegacyPage(data.configs, data.profile.configs, normalized, "profile membership");
|
|
1039
1195
|
}
|
|
1040
1196
|
async createProfile(input) {
|
|
1041
1197
|
const { data } = await this.request("POST", "/profiles", input, {
|
|
@@ -1059,6 +1215,10 @@ class CloudConfigStore {
|
|
|
1059
1215
|
await this.request("DELETE", `/profiles/${encodeURIComponent(profileIdOrSlug)}/configs/${encodeURIComponent(configId)}`, undefined, { allow404: true });
|
|
1060
1216
|
}
|
|
1061
1217
|
async resolveProfileForMachine(machine) {
|
|
1218
|
+
return (await this.resolveProfileForMachineRead(machine)).profile;
|
|
1219
|
+
}
|
|
1220
|
+
async resolveProfileForMachineRead(machine, options = {}) {
|
|
1221
|
+
const normalized = normalizeBoundedReadOptions(options);
|
|
1062
1222
|
const params = new URLSearchParams;
|
|
1063
1223
|
if (machine?.hostname)
|
|
1064
1224
|
params.set("hostname", machine.hostname);
|
|
@@ -1066,11 +1226,40 @@ class CloudConfigStore {
|
|
|
1066
1226
|
params.set("os", machine.os);
|
|
1067
1227
|
if (machine?.arch)
|
|
1068
1228
|
params.set("arch", machine.arch);
|
|
1229
|
+
params.set("limit", String(normalized.limit));
|
|
1069
1230
|
const qs = params.toString();
|
|
1070
1231
|
const { status, data } = await this.request("GET", `/profiles/resolve${qs ? `?${qs}` : ""}`, undefined, { allow404: true });
|
|
1071
|
-
if (status === 404
|
|
1072
|
-
return
|
|
1073
|
-
|
|
1232
|
+
if (status === 404) {
|
|
1233
|
+
return {
|
|
1234
|
+
profile: null,
|
|
1235
|
+
scanned: null,
|
|
1236
|
+
total: null,
|
|
1237
|
+
batch_limit: null,
|
|
1238
|
+
source_bounded: false,
|
|
1239
|
+
complete: true,
|
|
1240
|
+
truncated: false
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
if (data && "complete" in data) {
|
|
1244
|
+
if (data.complete !== true || data.truncated !== false) {
|
|
1245
|
+
throw new CloudHttpError(502, "profile resolve returned an incomplete or truncated read", data);
|
|
1246
|
+
}
|
|
1247
|
+
return { ...data, source_bounded: data.source_bounded ?? true };
|
|
1248
|
+
}
|
|
1249
|
+
if (data && "profile" in data) {
|
|
1250
|
+
return {
|
|
1251
|
+
profile: data.profile,
|
|
1252
|
+
scanned: null,
|
|
1253
|
+
total: null,
|
|
1254
|
+
batch_limit: null,
|
|
1255
|
+
source_bounded: false,
|
|
1256
|
+
complete: true,
|
|
1257
|
+
truncated: false
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
{
|
|
1261
|
+
throw new CloudHttpError(502, "profile resolve returned an incomplete or truncated read", data);
|
|
1262
|
+
}
|
|
1074
1263
|
}
|
|
1075
1264
|
async registerMachine(hostname2, os, arch2) {
|
|
1076
1265
|
const { data } = await this.request("POST", "/machines", { hostname: hostname2, os, arch: arch2 }, { idempotent: true });
|
|
@@ -12655,6 +12844,7 @@ export {
|
|
|
12655
12844
|
parseAgentOperatingRulesVersion,
|
|
12656
12845
|
now,
|
|
12657
12846
|
normalizeOsFamily,
|
|
12847
|
+
normalizeBoundedReadOptions,
|
|
12658
12848
|
machineContextToVariables,
|
|
12659
12849
|
isTemplate,
|
|
12660
12850
|
isCloudMode,
|
|
@@ -12684,6 +12874,7 @@ export {
|
|
|
12684
12874
|
buildOpenCodeAgentsMd,
|
|
12685
12875
|
buildCursorMdc,
|
|
12686
12876
|
buildCodexAgentsMd,
|
|
12877
|
+
boundedReadPage,
|
|
12687
12878
|
applyTransform,
|
|
12688
12879
|
applySessionRender,
|
|
12689
12880
|
applyProjectContext,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BoundedReadOptions, BoundedReadPage } from "../types/index.js";
|
|
2
|
+
export declare function normalizeBoundedReadOptions(options?: BoundedReadOptions): {
|
|
3
|
+
limit: number;
|
|
4
|
+
cursor: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function boundedReadPage<T>(items: T[], total: number, options?: BoundedReadOptions): BoundedReadPage<T>;
|
|
7
|
+
//# sourceMappingURL=bounded-read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bounded-read.d.ts","sourceRoot":"","sources":["../../src/lib/bounded-read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAE7E,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,kBAAuB,GAC/B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAKnC;AAED,wBAAgB,eAAe,CAAC,CAAC,EAC/B,KAAK,EAAE,CAAC,EAAE,EACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,kBAAuB,GAC/B,eAAe,CAAC,CAAC,CAAC,CAqBpB"}
|