@jsm-mit/sultana-agent-tools-package 0.2.0 → 0.4.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 +106 -8
- package/dist/confirmations.d.ts +61 -0
- package/dist/confirmations.d.ts.map +1 -0
- package/dist/confirmations.js +100 -0
- package/dist/create-salon-tools.d.ts +13 -2
- package/dist/create-salon-tools.d.ts.map +1 -1
- package/dist/create-salon-tools.js +10 -4
- package/dist/customer/create-customer-tools.d.ts +38 -0
- package/dist/customer/create-customer-tools.d.ts.map +1 -0
- package/dist/customer/create-customer-tools.js +47 -0
- package/dist/customer/customer-core-port.d.ts +94 -0
- package/dist/customer/customer-core-port.d.ts.map +1 -0
- package/dist/customer/customer-core-port.js +1 -0
- package/dist/customer/ic-customer-core-port.d.ts +45 -0
- package/dist/customer/ic-customer-core-port.d.ts.map +1 -0
- package/dist/customer/ic-customer-core-port.js +132 -0
- package/dist/customer/persona.d.ts +6 -0
- package/dist/customer/persona.d.ts.map +1 -0
- package/dist/customer/persona.js +27 -0
- package/dist/customer/time.d.ts +27 -0
- package/dist/customer/time.d.ts.map +1 -0
- package/dist/customer/time.js +113 -0
- package/dist/customer/tools.d.ts +17 -0
- package/dist/customer/tools.d.ts.map +1 -0
- package/dist/customer/tools.js +415 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/persona.d.ts +4 -1
- package/dist/persona.d.ts.map +1 -1
- package/dist/persona.js +18 -7
- package/dist/tools/promos.d.ts +2 -1
- package/dist/tools/promos.d.ts.map +1 -1
- package/dist/tools/promos.js +79 -39
- package/dist/tools/schedule.d.ts +2 -1
- package/dist/tools/schedule.d.ts.map +1 -1
- package/dist/tools/schedule.js +51 -31
- package/dist/tools/services.d.ts +4 -2
- package/dist/tools/services.d.ts.map +1 -1
- package/dist/tools/services.js +75 -30
- package/dist/tools/write-gate.d.ts +29 -0
- package/dist/tools/write-gate.d.ts.map +1 -0
- package/dist/tools/write-gate.js +37 -0
- package/package.json +4 -3
package/dist/tools/services.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { toToolError } from "../errors.js";
|
|
2
|
-
import { err,
|
|
2
|
+
import { err, ok } from "../types.js";
|
|
3
3
|
import { ArgumentError, normalizeDuration, normalizePrice, phrasesMatch, readBoolean, readNumber, readOptionalNumber, readOptionalString, readOptionalStringArray, readString, } from "./args.js";
|
|
4
|
+
import { writeGate } from "./write-gate.js";
|
|
4
5
|
const CONFIRMED_FIELD = {
|
|
5
6
|
type: "boolean",
|
|
6
7
|
description: "false dla podglądu: narzędzie nic nie zapisze i odda opis zmiany do zatwierdzenia przez właściciela. true dopiero po tym, jak właściciel potwierdzi.",
|
|
7
8
|
};
|
|
8
|
-
|
|
9
|
+
/** The fields of a service a model may set — what `update_service` echoes back when it was sent. */
|
|
10
|
+
const SERVICE_FIELDS = ["name", "pricePln", "durationMinutes", "serviceTypeIds", "workerIds", "active"];
|
|
11
|
+
export function createServiceTools(port, confirmations) {
|
|
9
12
|
return [
|
|
10
13
|
listServicesTool(port),
|
|
11
14
|
findServiceTypeTool(port),
|
|
12
15
|
listWorkersTool(port),
|
|
13
|
-
addServiceTool(port),
|
|
14
|
-
updateServiceTool(port),
|
|
15
|
-
removeServiceTool(port),
|
|
16
|
+
addServiceTool(port, confirmations),
|
|
17
|
+
updateServiceTool(port, confirmations),
|
|
18
|
+
removeServiceTool(port, confirmations),
|
|
16
19
|
];
|
|
17
20
|
}
|
|
18
21
|
/** What the model reads about the list depends on whose view the port returns: a public read has
|
|
@@ -51,6 +54,7 @@ export function listServicesTool(port, access = "owner") {
|
|
|
51
54
|
},
|
|
52
55
|
};
|
|
53
56
|
}
|
|
57
|
+
/** Takes any port that can search the catalogue — the customer set shares this tool. */
|
|
54
58
|
export function findServiceTypeTool(port) {
|
|
55
59
|
return {
|
|
56
60
|
name: "find_service_type",
|
|
@@ -102,12 +106,13 @@ function listWorkersTool(port) {
|
|
|
102
106
|
},
|
|
103
107
|
};
|
|
104
108
|
}
|
|
105
|
-
function addServiceTool(port) {
|
|
109
|
+
function addServiceTool(port, confirmations) {
|
|
110
|
+
const gate = writeGate("add_service", confirmations);
|
|
106
111
|
return {
|
|
107
112
|
name: "add_service",
|
|
108
113
|
progress: "Dodaję usługę…",
|
|
109
114
|
description: "Dodaje nową usługę do katalogu salonu. Najpierw ustal typ usługi przez find_service_type i pracowników przez list_workers.",
|
|
110
|
-
parameters: {
|
|
115
|
+
parameters: gate.parameters({
|
|
111
116
|
type: "object",
|
|
112
117
|
properties: {
|
|
113
118
|
name: { type: "string", description: "Nazwa usługi widoczna dla klientek." },
|
|
@@ -128,17 +133,23 @@ function addServiceTool(port) {
|
|
|
128
133
|
},
|
|
129
134
|
required: ["name", "pricePln", "durationMinutes", "confirmed"],
|
|
130
135
|
additionalProperties: false,
|
|
131
|
-
},
|
|
136
|
+
}),
|
|
132
137
|
execute: async (args) => {
|
|
133
138
|
try {
|
|
134
|
-
|
|
139
|
+
// The echo is the draft laid over the model's own arguments, and a draft read from an
|
|
140
|
+
// echo is that echo again — resending either one confirms the same change.
|
|
141
|
+
const draft = readDraft(args, null);
|
|
142
|
+
const echo = { ...args, ...draft.input };
|
|
143
|
+
const refusal = gate.refusal(args, echo);
|
|
144
|
+
if (refusal)
|
|
145
|
+
return refusal;
|
|
146
|
+
await assertDraftReferences(port, draft.input);
|
|
135
147
|
if (!readBoolean(args, "confirmed")) {
|
|
136
|
-
return
|
|
137
|
-
...args,
|
|
138
|
-
...draft.input,
|
|
139
|
-
confirmed: true,
|
|
140
|
-
});
|
|
148
|
+
return gate.preview(`Dodam usługę: ${await describe(port, draft.input)}.${noteSuffix(draft.notes)}`, echo);
|
|
141
149
|
}
|
|
150
|
+
const refused = gate.redeem(args, echo);
|
|
151
|
+
if (refused)
|
|
152
|
+
return refused;
|
|
142
153
|
const serviceId = await port.addService(draft.input);
|
|
143
154
|
return ok(`Dodano usługę „${draft.input.name}”.${noteSuffix(draft.notes)}`, { serviceId });
|
|
144
155
|
}
|
|
@@ -150,12 +161,13 @@ function addServiceTool(port) {
|
|
|
150
161
|
},
|
|
151
162
|
};
|
|
152
163
|
}
|
|
153
|
-
function updateServiceTool(port) {
|
|
164
|
+
function updateServiceTool(port, confirmations) {
|
|
165
|
+
const gate = writeGate("update_service", confirmations);
|
|
154
166
|
return {
|
|
155
167
|
name: "update_service",
|
|
156
168
|
progress: "Zmieniam usługę…",
|
|
157
169
|
description: "Zmienia istniejącą usługę. Podaj tylko te pola, które mają się zmienić — reszta zostanie zachowana. Id weź z list_services.",
|
|
158
|
-
parameters: {
|
|
170
|
+
parameters: gate.parameters({
|
|
159
171
|
type: "object",
|
|
160
172
|
properties: {
|
|
161
173
|
salonServiceId: { type: "string", description: "Id usługi z list_services." },
|
|
@@ -169,17 +181,27 @@ function updateServiceTool(port) {
|
|
|
169
181
|
},
|
|
170
182
|
required: ["salonServiceId", "confirmed"],
|
|
171
183
|
additionalProperties: false,
|
|
172
|
-
},
|
|
184
|
+
}),
|
|
173
185
|
execute: async (args) => {
|
|
174
186
|
try {
|
|
175
187
|
const serviceId = readString(args, "salonServiceId");
|
|
188
|
+
const echo = requestedServiceChange(serviceId, args);
|
|
189
|
+
const refusal = gate.refusal(args, echo);
|
|
190
|
+
if (refusal)
|
|
191
|
+
return refusal;
|
|
176
192
|
const current = await findService(port, serviceId);
|
|
177
193
|
if (!current)
|
|
178
194
|
return err("not_found", `W tym salonie nie ma usługi o id ${serviceId}.`);
|
|
179
|
-
|
|
195
|
+
// The summary shows the merged record the owner will end up with; the write merges
|
|
196
|
+
// again onto the record as it is by then.
|
|
197
|
+
const draft = readDraft(args, current);
|
|
198
|
+
await assertDraftReferences(port, draft.input);
|
|
180
199
|
if (!readBoolean(args, "confirmed")) {
|
|
181
|
-
return
|
|
200
|
+
return gate.preview(`Zmienię usługę „${current.name}” na: ${await describe(port, draft.input)}.${noteSuffix(draft.notes)}`, echo);
|
|
182
201
|
}
|
|
202
|
+
const refused = gate.redeem(args, echo);
|
|
203
|
+
if (refused)
|
|
204
|
+
return refused;
|
|
183
205
|
await port.updateService(serviceId, draft.input);
|
|
184
206
|
return ok(`Zmieniono usługę „${draft.input.name}”.${noteSuffix(draft.notes)}`);
|
|
185
207
|
}
|
|
@@ -191,12 +213,13 @@ function updateServiceTool(port) {
|
|
|
191
213
|
},
|
|
192
214
|
};
|
|
193
215
|
}
|
|
194
|
-
function removeServiceTool(port) {
|
|
216
|
+
function removeServiceTool(port, confirmations) {
|
|
217
|
+
const gate = writeGate("remove_service", confirmations);
|
|
195
218
|
return {
|
|
196
219
|
name: "remove_service",
|
|
197
220
|
progress: "Usuwam usługę…",
|
|
198
221
|
description: "Usuwa usługę z katalogu. Operacja jest nieodwracalna — właściciel musi przepisać dokładną nazwę usługi w polu confirmationPhrase.",
|
|
199
|
-
parameters: {
|
|
222
|
+
parameters: gate.parameters({
|
|
200
223
|
type: "object",
|
|
201
224
|
properties: {
|
|
202
225
|
salonServiceId: { type: "string", description: "Id usługi z list_services." },
|
|
@@ -205,20 +228,29 @@ function removeServiceTool(port) {
|
|
|
205
228
|
},
|
|
206
229
|
required: ["salonServiceId", "confirmed"],
|
|
207
230
|
additionalProperties: false,
|
|
208
|
-
},
|
|
231
|
+
}),
|
|
209
232
|
execute: async (args) => {
|
|
210
233
|
try {
|
|
211
234
|
const serviceId = readString(args, "salonServiceId");
|
|
235
|
+
// No confirmationPhrase in the echo: the name is the owner's to type, so a model that
|
|
236
|
+
// simply sends the echo back cannot delete anything.
|
|
237
|
+
const echo = { salonServiceId: serviceId };
|
|
238
|
+
const refusal = gate.refusal(args, echo);
|
|
239
|
+
if (refusal)
|
|
240
|
+
return refusal;
|
|
212
241
|
const current = await findService(port, serviceId);
|
|
213
242
|
if (!current)
|
|
214
243
|
return err("not_found", `W tym salonie nie ma usługi o id ${serviceId}.`);
|
|
215
244
|
if (!readBoolean(args, "confirmed")) {
|
|
216
|
-
return
|
|
245
|
+
return gate.preview(`Usunę usługę „${current.name}” (${current.pricePln} zł, ${current.durationMinutes} min). Tego nie da się cofnąć — poproś właścicielkę, żeby sama napisała dokładną nazwę usługi, i wstaw ją w confirmationPhrase.`, echo);
|
|
217
246
|
}
|
|
218
247
|
const phrase = readOptionalString(args, "confirmationPhrase");
|
|
219
248
|
if (!phrase || !phrasesMatch(phrase, current.name)) {
|
|
220
|
-
return err("invalid_arguments", `Aby usunąć usługę,
|
|
249
|
+
return err("invalid_arguments", `Aby usunąć usługę, właścicielka musi sama napisać jej dokładną nazwę: „${current.name}”.`);
|
|
221
250
|
}
|
|
251
|
+
const refused = gate.redeem(args, echo);
|
|
252
|
+
if (refused)
|
|
253
|
+
return refused;
|
|
222
254
|
await port.removeService(serviceId);
|
|
223
255
|
return ok(`Usunięto usługę „${current.name}”.`);
|
|
224
256
|
}
|
|
@@ -230,12 +262,23 @@ function removeServiceTool(port) {
|
|
|
230
262
|
},
|
|
231
263
|
};
|
|
232
264
|
}
|
|
265
|
+
/** `update_service`'s echo: the id and only the fields the model sent, as it sent them — never the
|
|
266
|
+
* merged record, which would pin fields the owner did not mention to what they were at preview. */
|
|
267
|
+
function requestedServiceChange(serviceId, args) {
|
|
268
|
+
const change = { salonServiceId: serviceId };
|
|
269
|
+
for (const field of SERVICE_FIELDS) {
|
|
270
|
+
if (args[field] !== undefined && args[field] !== null)
|
|
271
|
+
change[field] = args[field];
|
|
272
|
+
}
|
|
273
|
+
return change;
|
|
274
|
+
}
|
|
233
275
|
/**
|
|
234
|
-
* Builds the full record the canister needs. `current` is null for a new
|
|
235
|
-
* record for an edit — **this merge is the whole point**:
|
|
236
|
-
* field, so a "just the price" call that forgot `workerIds`
|
|
276
|
+
* Builds the full record the canister needs, without calling it. `current` is null for a new
|
|
277
|
+
* service and the existing record for an edit — **this merge is the whole point**:
|
|
278
|
+
* `updateSalonService` replaces every field, so a "just the price" call that forgot `workerIds`
|
|
279
|
+
* would unassign the entire team.
|
|
237
280
|
*/
|
|
238
|
-
|
|
281
|
+
function readDraft(args, current) {
|
|
239
282
|
const notes = [];
|
|
240
283
|
const name = current ? (readOptionalString(args, "name") ?? current.name) : readString(args, "name");
|
|
241
284
|
const rawPrice = readOptionalNumber(args, "pricePln");
|
|
@@ -249,10 +292,12 @@ async function buildDraft(port, args, current) {
|
|
|
249
292
|
const serviceTypeIds = readOptionalStringArray(args, "serviceTypeIds") ?? current?.serviceTypeIds ?? [];
|
|
250
293
|
const workerIds = readOptionalStringArray(args, "workerIds") ?? current?.workerIds ?? [];
|
|
251
294
|
const active = args.active === undefined || args.active === null ? (current?.active ?? true) : readBoolean(args, "active");
|
|
252
|
-
await assertKnownServiceTypes(port, serviceTypeIds);
|
|
253
|
-
await assertKnownWorkers(port, workerIds);
|
|
254
295
|
return { input: { name, pricePln: price, durationMinutes: duration, active, serviceTypeIds, workerIds }, notes };
|
|
255
296
|
}
|
|
297
|
+
async function assertDraftReferences(port, input) {
|
|
298
|
+
await assertKnownServiceTypes(port, input.serviceTypeIds);
|
|
299
|
+
await assertKnownWorkers(port, input.workerIds);
|
|
300
|
+
}
|
|
256
301
|
function normalizeOrNote(normalized, notes) {
|
|
257
302
|
if (normalized.note)
|
|
258
303
|
notes.push(normalized.note);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { TurnConfirmations } from "../confirmations.js";
|
|
2
|
+
import { type ToolParametersSchema, type ToolResult } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The confirmation protocol of ONE write tool, in one place for all of them.
|
|
5
|
+
*
|
|
6
|
+
* `echo` is the change as the tool describes it back to the model — the arguments to send again
|
|
7
|
+
* with `confirmed: true`; it defaults to `args` itself. A tool must build it from `args` the same
|
|
8
|
+
* way on both calls, and building it from an echo must give that echo back, so a model that resends
|
|
9
|
+
* its own arguments and a model that resends the echo confirm the same change.
|
|
10
|
+
*
|
|
11
|
+
* Without a ledger the gate lets every `confirmed: true` through, which is the protocol as it was
|
|
12
|
+
* before the ledger: the rule then lives in the persona only.
|
|
13
|
+
*/
|
|
14
|
+
export interface WriteGate {
|
|
15
|
+
/** The tool's schema, with `confirmationId` added when the tool set has a ledger. */
|
|
16
|
+
parameters(schema: ToolParametersSchema): ToolParametersSchema;
|
|
17
|
+
/** The `confirmed: false` answer: `echo` goes back with `confirmed: true` and, with a ledger,
|
|
18
|
+
* the id of this preview. */
|
|
19
|
+
preview(summary: string, echo: Record<string, unknown>): ToolResult;
|
|
20
|
+
/** Run first, before any canister call. For a `confirmed: true` call whose id would not redeem,
|
|
21
|
+
* the refusal to return; otherwise `null`. Does not use the id up, so a write that then fails
|
|
22
|
+
* on its arguments (a mistyped name, say) can be retried with the same id. */
|
|
23
|
+
refusal(args: Record<string, unknown>, echo?: Record<string, unknown>): ToolResult | null;
|
|
24
|
+
/** Run right before the write: uses the id up. `null` lets the write go ahead; a refusal here
|
|
25
|
+
* means a parallel call used the id first, or the preview expired in the meantime. */
|
|
26
|
+
redeem(args: Record<string, unknown>, echo?: Record<string, unknown>): ToolResult | null;
|
|
27
|
+
}
|
|
28
|
+
export declare function writeGate(toolName: string, confirmations: TurnConfirmations | undefined): WriteGate;
|
|
29
|
+
//# sourceMappingURL=write-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-gate.d.ts","sourceRoot":"","sources":["../../src/tools/write-gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqC,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAChG,OAAO,EAA0B,KAAK,oBAAoB,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAkBjG;;;;;;;;;;GAUG;AACH,MAAM,WAAW,SAAS;IACtB,qFAAqF;IACrF,UAAU,CAAC,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,CAAC;IAC/D;iCAC6B;IAC7B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAAC;IACpE;;kFAE8E;IAC9E,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC;IAC1F;0FACsF;IACtF,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC;CAC5F;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,iBAAiB,GAAG,SAAS,GAAG,SAAS,CA6BnG"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { err, needsConfirmation } from "../types.js";
|
|
2
|
+
import { readBoolean, readOptionalString } from "./args.js";
|
|
3
|
+
const CONFIRMATION_ID_FIELD = {
|
|
4
|
+
type: "string",
|
|
5
|
+
description: "Id z podglądu (confirmed=false). Wymagane przy confirmed=true.",
|
|
6
|
+
};
|
|
7
|
+
const NO_PREVIEW = "Brak ważnego podglądu tej zmiany — wywołaj narzędzie z confirmed=false i pokaż właścicielce podsumowanie.";
|
|
8
|
+
const REFUSALS = {
|
|
9
|
+
missing: NO_PREVIEW,
|
|
10
|
+
unknown: NO_PREVIEW,
|
|
11
|
+
expired: NO_PREVIEW,
|
|
12
|
+
same_turn: "Zgoda musi przyjść w osobnej wiadomości właścicielki — najpierw pokaż podsumowanie i poczekaj na odpowiedź.",
|
|
13
|
+
mismatch: "Argumenty różnią się od pokazanego podglądu — pokaż nowy podgląd (confirmed=false).",
|
|
14
|
+
};
|
|
15
|
+
export function writeGate(toolName, confirmations) {
|
|
16
|
+
const decide = (args, verdict) => {
|
|
17
|
+
if (!confirmations || !readBoolean(args, "confirmed"))
|
|
18
|
+
return null;
|
|
19
|
+
const result = verdict(confirmations, readOptionalString(args, "confirmationId"));
|
|
20
|
+
return result.ok ? null : err("invalid_arguments", REFUSALS[result.reason]);
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
parameters: (schema) => {
|
|
24
|
+
if (!confirmations)
|
|
25
|
+
return schema;
|
|
26
|
+
return { ...schema, properties: { ...schema.properties, confirmationId: CONFIRMATION_ID_FIELD } };
|
|
27
|
+
},
|
|
28
|
+
preview: (summary, echo) => {
|
|
29
|
+
if (!confirmations)
|
|
30
|
+
return needsConfirmation(summary, { ...echo, confirmed: true });
|
|
31
|
+
const confirmationId = confirmations.issue(toolName, echo);
|
|
32
|
+
return needsConfirmation(summary, { ...echo, confirmed: true, confirmationId });
|
|
33
|
+
},
|
|
34
|
+
refusal: (args, echo = args) => decide(args, (turn, confirmationId) => turn.check(toolName, echo, confirmationId)),
|
|
35
|
+
redeem: (args, echo = args) => decide(args, (turn, confirmationId) => turn.redeem(toolName, echo, confirmationId)),
|
|
36
|
+
};
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsm-mit/sultana-agent-tools-package",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Agent tool layer for
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Agent tool layer for Sultana: framework-agnostic tools over the Sultana core canister wrapper — a salon owner's set (services, promos, schedule) and a customer's set (search, free times, booking), each with a read-only set that needs no identity.",
|
|
5
5
|
"homepage": "https://github.com/JSM-Sultana/sultana-agent-tools-package#readme",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/JSM-Sultana/sultana-agent-tools-package/issues"
|
|
@@ -31,12 +31,13 @@
|
|
|
31
31
|
"sandbox": "npx tsx --env-file-if-exists=.env sandbox/main.ts",
|
|
32
32
|
"prepare": "npm run build",
|
|
33
33
|
"test-tools": "npx tsx --env-file-if-exists=.env --test tests/integration/test-salon-tools.ts",
|
|
34
|
+
"test-customer-tools": "npx tsx --env-file-if-exists=.env --test tests/integration/test-customer-tools.ts",
|
|
34
35
|
"whoami": "npx tsx --env-file-if-exists=.env sandbox/whoami.ts",
|
|
35
36
|
"publish-public": "bash scripts/publish-public.sh"
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
39
|
"@icp-sdk/core": "^6.1.0",
|
|
39
|
-
"@jsm-mit/sultana-core-motoko-package": "^0.
|
|
40
|
+
"@jsm-mit/sultana-core-motoko-package": "^0.21.1"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@jsm-mit/utils-package": "^0.5.0",
|