@ingram-cloud/pulumi 1.5.0 → 2.1.0
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/README.md +36 -1
- package/dist/resources.d.ts +33 -0
- package/dist/resources.js +163 -9
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -145,15 +145,50 @@ export const pageUrl = page.pageUrl; // https://cloud.ingram.tech/hosted/dep_…
|
|
|
145
145
|
> change to any of those (or to `agentId`) **replaces** the page and mints a fresh
|
|
146
146
|
> `pageUrl`. The agent's instructions change on the `IcAgent`, not here.
|
|
147
147
|
|
|
148
|
+
### IcSkill (declare in the APP repo)
|
|
149
|
+
|
|
150
|
+
An [Agent Skills](https://cloud.ingram.tech/docs/skills) bundle: a folder
|
|
151
|
+
anchored by `SKILL.md`, uploaded whole and versioned server-side. Declared by
|
|
152
|
+
**local directory** — the folder is the unit — like `IcAgent`'s `uiTemplates`
|
|
153
|
+
and the vector-store-shaped file attachments elsewhere in this pattern. The
|
|
154
|
+
bytes are read at `pulumi up`; any edit inside the folder publishes a new
|
|
155
|
+
immutable version and moves `default_version` to it, because a version is
|
|
156
|
+
immutable and nothing resolving by default would otherwise see the change.
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
// The folder is the unit. Its basename is the skill's name, and any edit
|
|
160
|
+
// inside it publishes a new immutable version and moves the default.
|
|
161
|
+
const vatFiling = new ic.IcSkill("vat-filing", {
|
|
162
|
+
...conn,
|
|
163
|
+
path: join(__dirname, "skills/vat-filing"),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Put it in front of a fleet. Order matters: a run that fills its search index
|
|
167
|
+
// keeps the earlier entries and drops the rest.
|
|
168
|
+
const biller = new ic.IcAgent("biller", {
|
|
169
|
+
...conn,
|
|
170
|
+
instructions: AGENT_SPECS.biller.instructions,
|
|
171
|
+
skills: [{ skill_id: vatFiling.skillId }],
|
|
172
|
+
// A skill's scripts/ only run through the run_command hosted tool.
|
|
173
|
+
enabledHostedTools: ["run_command"],
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
148
177
|
### IcMcpServer, IcOauthProvider, IcTelegramBot, IcWhatsAppConfig, IcEmail, IcWebhook, IcModelKey (declare in INFRA)
|
|
149
178
|
|
|
150
179
|
```ts
|
|
151
180
|
// Your own server (raw URL).
|
|
152
181
|
new ic.IcMcpServer("thornhill-mcp", {
|
|
153
182
|
...conn, serverName: "thornhill", url: `${APP_URL}/api/mcp`,
|
|
154
|
-
authKind: "
|
|
183
|
+
authKind: "bearer", secret: mcpSecret,
|
|
155
184
|
});
|
|
156
185
|
|
|
186
|
+
// The tool list is discovered at create, not on every `pulumi up` — nothing in
|
|
187
|
+
// this stack changes when your *server's* tools do, so `toolsDiscovered` is what
|
|
188
|
+
// was true at registration. Ingram Cloud re-discovers on the run path once a
|
|
189
|
+
// manifest is an hour old; call `POST /v1/tenant/mcp/{serverName}/refresh` at the
|
|
190
|
+
// end of a deploy that changed your tools if an hour is too long to wait.
|
|
191
|
+
|
|
157
192
|
// An `oauth`-kind server forwards each end-user's own stored token; its
|
|
158
193
|
// provider record tells IC how tokens refresh. Webhook-delegation mode (a
|
|
159
194
|
// refreshWebhook and NO clientSecret): IC POSTs { connection_id } to the
|
package/dist/resources.d.ts
CHANGED
|
@@ -37,6 +37,13 @@ export interface AgentVariable {
|
|
|
37
37
|
description?: string | null;
|
|
38
38
|
required?: boolean;
|
|
39
39
|
}
|
|
40
|
+
/** One skill reference in an agent's `skills` array. Omitted `version` follows
|
|
41
|
+
* the skill's `default_version` and is frozen at publish. Pair with
|
|
42
|
+
* {@link IcSkill}'s `skillId` output. */
|
|
43
|
+
export interface AgentSkillRef {
|
|
44
|
+
skillId: string;
|
|
45
|
+
version?: number;
|
|
46
|
+
}
|
|
40
47
|
/** CSP domain allowlists for the sandboxed iframe (the `_meta.ui.csp` shape). */
|
|
41
48
|
export interface UiCsp {
|
|
42
49
|
connectDomains?: string[];
|
|
@@ -97,6 +104,15 @@ export interface IcAgentArgs {
|
|
|
97
104
|
/** Opt into observational-memory consolidation (background, billed). Default off. */
|
|
98
105
|
memoryConsolidation?: pulumi.Input<boolean>;
|
|
99
106
|
variables?: pulumi.Input<AgentVariable[]>;
|
|
107
|
+
/**
|
|
108
|
+
* Skills (`skl_…`) attached to the agent, in order. Order is not cosmetic: a
|
|
109
|
+
* run whose skills together exceed the search-index ceiling keeps them in
|
|
110
|
+
* this order and drops the rest, so it is the survival priority. Folds into
|
|
111
|
+
* the content signature unsorted, unlike `enabledHostedTools`/`vectorStoreIds`
|
|
112
|
+
* — reordering is itself a content change. Pair with {@link IcSkill}'s
|
|
113
|
+
* `skillId` output.
|
|
114
|
+
*/
|
|
115
|
+
skills?: pulumi.Input<AgentSkillRef[]>;
|
|
100
116
|
/**
|
|
101
117
|
* MCP Apps UI templates attached to the agent. Each template's HTML bundle is
|
|
102
118
|
* uploaded when its bytes or metadata change, and its name + content hash +
|
|
@@ -117,6 +133,23 @@ export declare class IcAgent extends pulumi.dynamic.Resource {
|
|
|
117
133
|
readonly activeVersion: pulumi.Output<number>;
|
|
118
134
|
constructor(name: string, args: IcAgentArgs, opts?: pulumi.CustomResourceOptions);
|
|
119
135
|
}
|
|
136
|
+
export interface IcSkillArgs {
|
|
137
|
+
baseUrl: pulumi.Input<string>;
|
|
138
|
+
token: pulumi.Input<string>;
|
|
139
|
+
/**
|
|
140
|
+
* Absolute path to the skill's directory — the folder anchored by `SKILL.md`,
|
|
141
|
+
* whose basename is the skill's name. Read at `pulumi up`; edit any file in it
|
|
142
|
+
* and a new immutable version is published and made the default.
|
|
143
|
+
*/
|
|
144
|
+
path: pulumi.Input<string>;
|
|
145
|
+
}
|
|
146
|
+
export declare class IcSkill extends pulumi.dynamic.Resource {
|
|
147
|
+
/** The `skl_…` id (same as `.id`, exposed for convenience). */
|
|
148
|
+
readonly skillId: pulumi.Output<string>;
|
|
149
|
+
/** The version this stack last published, and the current `default_version`. */
|
|
150
|
+
readonly version: pulumi.Output<number>;
|
|
151
|
+
constructor(name: string, args: IcSkillArgs, opts?: pulumi.CustomResourceOptions);
|
|
152
|
+
}
|
|
120
153
|
/** One source file on an {@link IcVectorStore}, provided by local path. */
|
|
121
154
|
export interface VectorStoreFile {
|
|
122
155
|
/** Absolute path to the file to ingest (read at `pulumi up`, like `htmlPath`). */
|
package/dist/resources.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.IcWebPage = exports.IcProjectToken = exports.IcProject = exports.IcModelKey = exports.IcWebhook = exports.IcEmail = exports.IcWhatsAppConfig = exports.IcOauthProvider = exports.IcTelegramBot = exports.IcMcpServer = exports.IcVectorStore = exports.IcAgent = exports.IC_API_VERSION = void 0;
|
|
36
|
+
exports.IcWebPage = exports.IcProjectToken = exports.IcProject = exports.IcModelKey = exports.IcWebhook = exports.IcEmail = exports.IcWhatsAppConfig = exports.IcOauthProvider = exports.IcTelegramBot = exports.IcMcpServer = exports.IcVectorStore = exports.IcSkill = exports.IcAgent = exports.IC_API_VERSION = void 0;
|
|
37
37
|
/**
|
|
38
38
|
* Ingram Cloud /v1 tenant configuration as Pulumi dynamic resources.
|
|
39
39
|
*
|
|
@@ -248,6 +248,10 @@ function agentBody(i) {
|
|
|
248
248
|
auto_memory: i.autoMemory ?? null,
|
|
249
249
|
memory_consolidation: i.memoryConsolidation ?? null,
|
|
250
250
|
variables: i.variables ?? [],
|
|
251
|
+
skills: (i.skills ?? []).map((s) => ({
|
|
252
|
+
skill_id: s.skillId,
|
|
253
|
+
...(s.version != null ? { version: s.version } : {}),
|
|
254
|
+
})),
|
|
251
255
|
};
|
|
252
256
|
}
|
|
253
257
|
// Canonical, name-sorted signature of an agent's frozen UI templates: each
|
|
@@ -291,6 +295,12 @@ function agentContentSig(s) {
|
|
|
291
295
|
const vs = [...(s.vector_store_ids ?? [])].sort();
|
|
292
296
|
if (vs.length)
|
|
293
297
|
base.vector_store_ids = vs;
|
|
298
|
+
// Skills, deliberately NOT sorted: reference order is the run's index-ceiling
|
|
299
|
+
// drop priority, so reordering the array is itself a content change worth a
|
|
300
|
+
// new version, not a no-op like reordering hosted tools or vector stores is.
|
|
301
|
+
const skills = s.skills ?? [];
|
|
302
|
+
if (skills.length)
|
|
303
|
+
base.skills = skills;
|
|
294
304
|
return JSON.stringify(base);
|
|
295
305
|
}
|
|
296
306
|
// Map the camelCase resource inputs onto the canonical snapshot shape.
|
|
@@ -304,6 +314,10 @@ function inputsToSnapshot(i) {
|
|
|
304
314
|
auto_memory: i.autoMemory,
|
|
305
315
|
memory_consolidation: i.memoryConsolidation,
|
|
306
316
|
variables: i.variables,
|
|
317
|
+
skills: (i.skills ?? []).map((s) => ({
|
|
318
|
+
skill_id: s.skillId,
|
|
319
|
+
...(s.version != null ? { version: s.version } : {}),
|
|
320
|
+
})),
|
|
307
321
|
// Resolve each template's bytes and hash so a HTML edit shows up in the
|
|
308
322
|
// signature (the API's `content_hash` uses the same sha256-hex).
|
|
309
323
|
ui_resources: resolveUiTemplates(i.uiTemplates).map((t) => ({
|
|
@@ -316,7 +330,7 @@ function inputsToSnapshot(i) {
|
|
|
316
330
|
};
|
|
317
331
|
}
|
|
318
332
|
async function findAgentBySlug(baseUrl, token, slug) {
|
|
319
|
-
const res = await icRequest(baseUrl, token, "GET", "/v1/agents");
|
|
333
|
+
const res = await icRequest(baseUrl, token, "GET", "/v1/agents?limit=200");
|
|
320
334
|
return (res.data ?? []).find((b) => b.slug === slug) ?? null;
|
|
321
335
|
}
|
|
322
336
|
// Signature of the snapshot currently published as the active version, or null
|
|
@@ -324,7 +338,7 @@ async function findAgentBySlug(baseUrl, token, slug) {
|
|
|
324
338
|
async function activeSnapshotSig(baseUrl, token, id, activeVersion) {
|
|
325
339
|
if (!activeVersion)
|
|
326
340
|
return null;
|
|
327
|
-
const res = await icRequest(baseUrl, token, "GET", `/v1/agents/${id}/versions`);
|
|
341
|
+
const res = await icRequest(baseUrl, token, "GET", `/v1/agents/${id}/versions?limit=200`);
|
|
328
342
|
const v = (res.data ?? []).find((x) => x.version === activeVersion);
|
|
329
343
|
if (!v)
|
|
330
344
|
return null;
|
|
@@ -451,6 +465,10 @@ const agentProvider = {
|
|
|
451
465
|
autoMemory: d.auto_memory ?? undefined,
|
|
452
466
|
memoryConsolidation: d.memory_consolidation ?? undefined,
|
|
453
467
|
variables: d.variables ?? [],
|
|
468
|
+
skills: (d.skills ?? []).map((s) => ({
|
|
469
|
+
skillId: s.skill_id,
|
|
470
|
+
version: s.version,
|
|
471
|
+
})),
|
|
454
472
|
},
|
|
455
473
|
};
|
|
456
474
|
});
|
|
@@ -467,6 +485,7 @@ class IcAgent extends pulumi.dynamic.Resource {
|
|
|
467
485
|
autoMemory: undefined,
|
|
468
486
|
memoryConsolidation: undefined,
|
|
469
487
|
variables: undefined,
|
|
488
|
+
skills: undefined,
|
|
470
489
|
uiTemplates: undefined,
|
|
471
490
|
publishNote: undefined,
|
|
472
491
|
rolloutPercent: undefined,
|
|
@@ -481,6 +500,128 @@ class IcAgent extends pulumi.dynamic.Resource {
|
|
|
481
500
|
}
|
|
482
501
|
}
|
|
483
502
|
exports.IcAgent = IcAgent;
|
|
503
|
+
/** Walk a skill directory into the parts the upload sends.
|
|
504
|
+
*
|
|
505
|
+
* `require` inline rather than a top-level import: Pulumi serializes a dynamic
|
|
506
|
+
* provider's closure, and this is the idiom the vector-store resource next door
|
|
507
|
+
* already uses to read local files. */
|
|
508
|
+
function resolveSkillFiles(dir) {
|
|
509
|
+
if (!dir)
|
|
510
|
+
throw new Error("skill: `path` is required");
|
|
511
|
+
const fs = require("node:fs");
|
|
512
|
+
const nodePath = require("node:path");
|
|
513
|
+
const root = nodePath.basename(nodePath.resolve(dir));
|
|
514
|
+
const out = [];
|
|
515
|
+
const walk = (at, prefix) => {
|
|
516
|
+
for (const entry of fs.readdirSync(at, { withFileTypes: true })) {
|
|
517
|
+
const full = nodePath.join(at, entry.name);
|
|
518
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
519
|
+
if (entry.isDirectory())
|
|
520
|
+
walk(full, rel);
|
|
521
|
+
else if (entry.isFile())
|
|
522
|
+
out.push({ rel: `${root}/${rel}`, bytes: fs.readFileSync(full) });
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
walk(nodePath.resolve(dir), "");
|
|
526
|
+
if (!out.some((f) => f.rel === `${root}/SKILL.md`))
|
|
527
|
+
throw new Error(`skill: ${dir} has no SKILL.md`);
|
|
528
|
+
return out.sort((a, b) => (a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0));
|
|
529
|
+
}
|
|
530
|
+
/** A content signature over the whole bundle — what `diff` compares. */
|
|
531
|
+
function skillSig(dir) {
|
|
532
|
+
if (!dir)
|
|
533
|
+
return "";
|
|
534
|
+
const { createHash } = require("node:crypto");
|
|
535
|
+
const hash = createHash("sha256");
|
|
536
|
+
for (const file of resolveSkillFiles(dir)) {
|
|
537
|
+
hash.update(file.rel);
|
|
538
|
+
hash.update(file.bytes);
|
|
539
|
+
}
|
|
540
|
+
return hash.digest("hex");
|
|
541
|
+
}
|
|
542
|
+
/** Upload a bundle as repeated `files[]` parts. Not `icRequest` (that
|
|
543
|
+
* JSON-encodes); uses global FormData/File (Node ≥18), like `uploadFile`. */
|
|
544
|
+
async function uploadSkillBundle(baseUrl, token, path, dir) {
|
|
545
|
+
const form = new FormData();
|
|
546
|
+
for (const file of resolveSkillFiles(dir))
|
|
547
|
+
form.append("files[]", new File([file.bytes], file.rel));
|
|
548
|
+
const res = await fetch(`${baseUrl}${path}`, {
|
|
549
|
+
method: "POST",
|
|
550
|
+
headers: {
|
|
551
|
+
Authorization: `Bearer ${token}`,
|
|
552
|
+
"IC-Api-Version": exports.IC_API_VERSION,
|
|
553
|
+
},
|
|
554
|
+
body: form,
|
|
555
|
+
});
|
|
556
|
+
const text = await res.text();
|
|
557
|
+
if (!res.ok) {
|
|
558
|
+
const err = new Error(`IC POST ${path} → ${res.status}: ${text.slice(0, 400)}`);
|
|
559
|
+
err.status = res.status;
|
|
560
|
+
throw err;
|
|
561
|
+
}
|
|
562
|
+
return text ? JSON.parse(text) : {};
|
|
563
|
+
}
|
|
564
|
+
const skillProvider = {
|
|
565
|
+
async create(i) {
|
|
566
|
+
const skill = await uploadSkillBundle(i.baseUrl, i.token, "/v1/skills", i.path);
|
|
567
|
+
return {
|
|
568
|
+
id: skill.id,
|
|
569
|
+
outs: {
|
|
570
|
+
...i,
|
|
571
|
+
skillId: skill.id,
|
|
572
|
+
version: skill.default_version,
|
|
573
|
+
contentSig: skillSig(i.path),
|
|
574
|
+
},
|
|
575
|
+
};
|
|
576
|
+
},
|
|
577
|
+
async update(id, _olds, news) {
|
|
578
|
+
// A version is immutable, so an edited bundle is a new one — and the
|
|
579
|
+
// default has to move, or nothing that resolves by default would see it.
|
|
580
|
+
const published = await uploadSkillBundle(news.baseUrl, news.token, `/v1/skills/${id}/versions`, news.path);
|
|
581
|
+
await icRequest(news.baseUrl, news.token, "POST", `/v1/skills/${id}`, {
|
|
582
|
+
default_version: published.version,
|
|
583
|
+
});
|
|
584
|
+
return {
|
|
585
|
+
outs: {
|
|
586
|
+
...news,
|
|
587
|
+
skillId: id,
|
|
588
|
+
version: published.version,
|
|
589
|
+
contentSig: skillSig(news.path),
|
|
590
|
+
},
|
|
591
|
+
};
|
|
592
|
+
},
|
|
593
|
+
async delete(id, props) {
|
|
594
|
+
await icRequest(props.baseUrl, props.token, "DELETE", `/v1/skills/${id}`);
|
|
595
|
+
},
|
|
596
|
+
async diff(_id, olds, news) {
|
|
597
|
+
// Compare against the sig STORED at the last apply, not a re-resolve of
|
|
598
|
+
// olds: olds.path points at the same folder, whose bytes have already
|
|
599
|
+
// changed, so re-reading it would hide the change (same reasoning as the
|
|
600
|
+
// agent and vector-store diffs).
|
|
601
|
+
const changed = (olds.contentSig ?? "") !== skillSig(news.path);
|
|
602
|
+
return { changes: changed };
|
|
603
|
+
},
|
|
604
|
+
async read(id, props) {
|
|
605
|
+
return read404(() => icRequest(props.baseUrl, props.token, "GET", `/v1/skills/${id}`), (skill) => {
|
|
606
|
+
if (!skill || !skill.id)
|
|
607
|
+
return { id: "" };
|
|
608
|
+
return {
|
|
609
|
+
id,
|
|
610
|
+
props: {
|
|
611
|
+
...props,
|
|
612
|
+
skillId: skill.id,
|
|
613
|
+
version: skill.default_version,
|
|
614
|
+
},
|
|
615
|
+
};
|
|
616
|
+
});
|
|
617
|
+
},
|
|
618
|
+
};
|
|
619
|
+
class IcSkill extends pulumi.dynamic.Resource {
|
|
620
|
+
constructor(name, args, opts) {
|
|
621
|
+
super(skillProvider, name, { skillId: undefined, version: undefined, contentSig: undefined, ...args }, { ...opts, additionalSecretOutputs: ["token"] });
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
exports.IcSkill = IcSkill;
|
|
484
625
|
// Read each declared file's bytes and hash them (same sha256-hex the API keys
|
|
485
626
|
// files by) so an edit shows up in the file signature. Module-scope + node
|
|
486
627
|
// built-ins required inside for Pulumi's closure serialization.
|
|
@@ -505,7 +646,9 @@ function resolveVsFiles(files) {
|
|
|
505
646
|
// JSON-encodes the body); uses global FormData/File (Node ≥18).
|
|
506
647
|
async function uploadFile(baseUrl, token, bytes, filename) {
|
|
507
648
|
const form = new FormData();
|
|
508
|
-
form.set("file", new File([new Uint8Array(bytes)], filename, {
|
|
649
|
+
form.set("file", new File([new Uint8Array(bytes)], filename, {
|
|
650
|
+
type: "application/octet-stream",
|
|
651
|
+
}));
|
|
509
652
|
form.set("purpose", "assistants");
|
|
510
653
|
const res = await fetch(`${baseUrl}/v1/files`, {
|
|
511
654
|
method: "POST",
|
|
@@ -583,7 +726,12 @@ const vectorStoreProvider = {
|
|
|
583
726
|
const fileState = await syncVsFiles(i, res.id, []);
|
|
584
727
|
return {
|
|
585
728
|
id: res.id,
|
|
586
|
-
outs: {
|
|
729
|
+
outs: {
|
|
730
|
+
...i,
|
|
731
|
+
vectorStoreId: res.id,
|
|
732
|
+
fileState,
|
|
733
|
+
filesSig: vsFilesSig(i.files),
|
|
734
|
+
},
|
|
587
735
|
};
|
|
588
736
|
},
|
|
589
737
|
async update(id, olds, news) {
|
|
@@ -592,7 +740,12 @@ const vectorStoreProvider = {
|
|
|
592
740
|
await icRequest(news.baseUrl, news.token, "POST", `/v1/vector_stores/${id}`, body);
|
|
593
741
|
const fileState = await syncVsFiles(news, id, olds.fileState ?? []);
|
|
594
742
|
return {
|
|
595
|
-
outs: {
|
|
743
|
+
outs: {
|
|
744
|
+
...news,
|
|
745
|
+
vectorStoreId: id,
|
|
746
|
+
fileState,
|
|
747
|
+
filesSig: vsFilesSig(news.files),
|
|
748
|
+
},
|
|
596
749
|
};
|
|
597
750
|
},
|
|
598
751
|
async delete(id, props) {
|
|
@@ -601,7 +754,8 @@ const vectorStoreProvider = {
|
|
|
601
754
|
async diff(_id, olds, news) {
|
|
602
755
|
const scalarChanged = (olds.name ?? undefined) !== (news.name ?? undefined) ||
|
|
603
756
|
(olds.description ?? undefined) !== (news.description ?? undefined);
|
|
604
|
-
const metaChanged = JSON.stringify(olds.metadata ?? null) !==
|
|
757
|
+
const metaChanged = JSON.stringify(olds.metadata ?? null) !==
|
|
758
|
+
JSON.stringify(news.metadata ?? null);
|
|
605
759
|
// Compare against the sig STORED at the last apply (not a re-resolve of olds):
|
|
606
760
|
// a file-backed path edits the same bytes, so re-reading olds would hash the
|
|
607
761
|
// new bytes and hide the change (same reasoning as the agent diff).
|
|
@@ -703,7 +857,7 @@ const mcpProvider = makePutResource({
|
|
|
703
857
|
class IcMcpServer extends pulumi.dynamic.Resource {
|
|
704
858
|
constructor(name, args, opts) {
|
|
705
859
|
super(mcpProvider, name, {
|
|
706
|
-
authKind: args.catalog ? undefined : "
|
|
860
|
+
authKind: args.catalog ? undefined : "bearer",
|
|
707
861
|
url: undefined,
|
|
708
862
|
catalog: undefined,
|
|
709
863
|
authProvider: undefined,
|
|
@@ -891,7 +1045,7 @@ const webhookProvider = {
|
|
|
891
1045
|
// Enables `pulumi import` of a webhook a script already created (id is the
|
|
892
1046
|
// import id). The signing secret is intentionally not recoverable.
|
|
893
1047
|
async read(id, props) {
|
|
894
|
-
const list = await icRequest(props.baseUrl, props.token, "GET", "/v1/tenant/webhooks");
|
|
1048
|
+
const list = await icRequest(props.baseUrl, props.token, "GET", "/v1/tenant/webhooks?limit=200");
|
|
895
1049
|
const hit = (list.data ?? []).find((w) => w.id === id);
|
|
896
1050
|
// Absent from the list → gone server-side; empty id lets refresh prune it.
|
|
897
1051
|
if (!hit)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ingram-cloud/pulumi",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Pulumi dynamic resources for the Ingram Cloud /v1 API — projects, project tokens, agents, MCP servers, channels, webhooks, model keys.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://cloud.ingram.tech",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@ingram-tech/oxlint-config": "^0.2.2",
|
|
32
|
-
"@pulumi/pulumi": "^3.
|
|
33
|
-
"@types/node": "^26.1.
|
|
34
|
-
"oxfmt": "
|
|
35
|
-
"oxlint": "^1.
|
|
32
|
+
"@pulumi/pulumi": "^3.255.0",
|
|
33
|
+
"@types/node": "^26.1.2",
|
|
34
|
+
"oxfmt": "0.62.0",
|
|
35
|
+
"oxlint": "^1.77.0",
|
|
36
36
|
"typescript": "^7.0.2"
|
|
37
37
|
}
|
|
38
38
|
}
|