perstack 0.0.108 → 0.0.110

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/bin/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM, r as __exportAll, t as __commonJSMin } from "../chunk-D_gEzPfs.js";
3
- import { $ as lazy, A as runCommandInputSchema, At as PerstackError, B as resolveModelTier, C as runSettingSchema, Ct as safeParse$1, D as stopRunByDelegate, Dt as normalizeParams, E as startRun, Et as defineLazy, F as expertSchema, G as _instanceof, H as ZodIssueCode$1, I as isCoordinatorExpert, J as array$1, K as _null, L as validateDelegation, M as perstackConfigSchema, Mt as defaultPerstackApiBaseUrl, N as lockfileSchema, Nt as defaultTimeout, O as stopRunByError, Ot as $constructor, P as jobSchema, Pt as createId, Q as intersection, R as checkpointSchema, S as runParamsSchema, St as parseAsync, T as startGeneration, Tt as clone, U as ZodOptional$1, V as number$1, W as _enum, X as custom, Y as boolean, Z as discriminatedUnion, _ as parseExpertKey, _t as describe$1, a as validateEventFilter, at as optional, b as resumeFromStop, bt as $ZodType, c as createBaseToolActivity, ct as strictObject, d as completeRun, dt as union, et as literal, f as continueToNextStep, ft as unknown, g as finishToolCall, gt as toJSONSchema, h as finishMcpTools, ht as datetime, i as createFilteredEventListener, it as object$2, j as startCommandInputSchema, jt as defaultMaxRetries, k as stopRunByInteractiveTool, kt as NEVER, l as createGeneralToolActivity, lt as string, m as createStreamingEvent, mt as safeParseAsync$2, n as parseWithFriendlyError, nt as never, o as getFilteredEnv, ot as preprocess, p as createRuntimeEvent, pt as url, q as any, r as truncateText$1, rt as number, s as BASE_SKILL_PREFIX, st as record, t as createApiClient, tt as looseObject, u as callTools, ut as tuple, v as proceedToInteractiveTools, vt as meta$1, w as skipDelegates, wt as safeParseAsync$1, x as retry, xt as parse$1, y as resolveToolResults, yt as $ZodObject, z as knownModels } from "../dist-Bm8UQoRz.js";
3
+ import { $ as lazy, A as runCommandInputSchema, At as PerstackError, B as resolveModelTier, C as runSettingSchema, Ct as safeParse$1, D as stopRunByDelegate, Dt as normalizeParams, E as startRun, Et as defineLazy, F as expertSchema, G as _instanceof, H as ZodIssueCode$1, I as isCoordinatorExpert, J as array$1, K as _null, L as validateDelegation, M as perstackConfigSchema, Mt as defaultPerstackApiBaseUrl, N as lockfileSchema, Nt as defaultTimeout, O as stopRunByError, Ot as $constructor, P as jobSchema, Pt as createId, Q as intersection, R as checkpointSchema, S as runParamsSchema, St as parseAsync, T as startGeneration, Tt as clone, U as ZodOptional$1, V as number$1, W as _enum, X as custom, Y as boolean, Z as discriminatedUnion, _ as parseExpertKey, _t as describe$1, a as validateEventFilter, at as optional, b as resumeFromStop, bt as $ZodType, c as createBaseToolActivity, ct as strictObject, d as completeRun, dt as union, et as literal, f as continueToNextStep, ft as unknown, g as finishToolCall, gt as toJSONSchema, h as finishMcpTools, ht as datetime, i as createFilteredEventListener, it as object$2, j as startCommandInputSchema, jt as defaultMaxRetries, k as stopRunByInteractiveTool, kt as NEVER, l as createGeneralToolActivity, lt as string, m as createStreamingEvent, mt as safeParseAsync$2, n as parseWithFriendlyError, nt as never, o as getFilteredEnv, ot as preprocess, p as createRuntimeEvent, pt as url, q as any, r as truncateText$1, rt as number, s as BASE_SKILL_PREFIX, st as record, t as createApiClient, tt as looseObject, u as callTools, ut as tuple, v as proceedToInteractiveTools, vt as meta$1, w as skipDelegates, wt as safeParseAsync$1, x as retry, xt as parse$1, y as resolveToolResults, yt as $ZodObject, z as knownModels } from "../dist-C-YhEwlg.js";
4
4
  import { t as require_token_error } from "../token-error-_b-fOBh2.js";
5
5
  import fs, { constants, lstat, mkdir, open, readFile, stat, writeFile } from "node:fs/promises";
6
6
  import path, { dirname, extname } from "node:path";
@@ -16302,7 +16302,7 @@ const EMPTY_COMPLETION_RESULT = { completion: {
16302
16302
  //#endregion
16303
16303
  //#region ../base/package.json
16304
16304
  var name$5 = "@perstack/base";
16305
- var version$2 = "0.0.70";
16305
+ var version$2 = "0.0.72";
16306
16306
 
16307
16307
  //#endregion
16308
16308
  //#region ../base/src/tools/todo.ts
@@ -20882,9 +20882,9 @@ function formatJobHeader(job) {
20882
20882
  const lines = [];
20883
20883
  lines.push(`Job: ${job.id} (${job.status})`);
20884
20884
  lines.push(`Expert: ${job.coordinatorExpertKey}`);
20885
- lines.push(`Started: ${formatTimestamp$1(job.startedAt)}`);
20885
+ lines.push(`Started: ${formatTimestamp$2(job.startedAt)}`);
20886
20886
  lines.push(`Steps: ${job.totalSteps}`);
20887
- if (job.finishedAt) lines.push(`Finished: ${formatTimestamp$1(job.finishedAt)}`);
20887
+ if (job.finishedAt) lines.push(`Finished: ${formatTimestamp$2(job.finishedAt)}`);
20888
20888
  return lines;
20889
20889
  }
20890
20890
  function formatCheckpointHeader(checkpoint) {
@@ -20977,7 +20977,7 @@ function extractTextContent$1(contents) {
20977
20977
  for (const c of contents) if (typeof c === "object" && c !== null && "type" in c && c.type === "textPart" && "text" in c) return String(c.text);
20978
20978
  return "";
20979
20979
  }
20980
- function formatTimestamp$1(ts) {
20980
+ function formatTimestamp$2(ts) {
20981
20981
  return new Date(ts).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
20982
20982
  }
20983
20983
  function formatTime(ts) {
@@ -21310,7 +21310,7 @@ async function expertVersionsHandler(scopeName, options) {
21310
21310
 
21311
21311
  //#endregion
21312
21312
  //#region ../../packages/runtime/package.json
21313
- var version$1 = "0.0.123";
21313
+ var version$1 = "0.0.125";
21314
21314
 
21315
21315
  //#endregion
21316
21316
  //#region ../../packages/runtime/src/helpers/usage.ts
@@ -77271,7 +77271,7 @@ function validateRuntimeVersion(experts) {
77271
77271
  //#endregion
77272
77272
  //#region ../../packages/runtime/src/helpers/setup-experts.ts
77273
77273
  async function setupExperts(setting, resolveExpertToRun) {
77274
- const resolveFn = resolveExpertToRun ?? (await import("../resolve-expert-CwaeoRiD.js")).resolveExpertToRun;
77274
+ const resolveFn = resolveExpertToRun ?? (await import("../resolve-expert-CVjfQVXn.js")).resolveExpertToRun;
77275
77275
  const { expertKey } = setting;
77276
77276
  const experts = { ...setting.experts };
77277
77277
  const clientOptions = {
@@ -90819,11 +90819,13 @@ var CoordinatorExecutor = class {
90819
90819
  }
90820
90820
  async execute(setting, checkpoint) {
90821
90821
  const adapter = await createProviderAdapter(setting.providerConfig, { proxyUrl: setting.proxyUrl });
90822
- const llmExecutor = new LLMExecutor(adapter, adapter.createModel(setting.model));
90823
- const contextWindow = getContextWindow(setting.providerConfig.providerName, setting.model);
90824
90822
  const { expertToRun, experts } = await setupExperts(setting, this.options.resolveExpertToRun);
90825
90823
  validateRuntimeVersion(experts);
90826
- this.emitInitEvent(setting, expertToRun, experts);
90824
+ const resolvedModel = setting.model ?? (expertToRun.defaultModelTier ? resolveModelTier(setting.providerConfig.providerName, expertToRun.defaultModelTier) : void 0) ?? resolveModelTier(setting.providerConfig.providerName, "middle");
90825
+ if (!resolvedModel) throw new PerstackError(`Cannot resolve model for provider "${setting.providerConfig.providerName}". Specify a model explicitly with --model.`);
90826
+ const llmExecutor = new LLMExecutor(adapter, adapter.createModel(resolvedModel));
90827
+ const contextWindow = getContextWindow(setting.providerConfig.providerName, resolvedModel);
90828
+ this.emitInitEvent(setting, resolvedModel, expertToRun, experts);
90827
90829
  const onLifecycleEvent = this.createLifecycleEventBridge(setting);
90828
90830
  const lockfileExpert = this.options.lockfile?.experts[setting.expertKey];
90829
90831
  const skillManager = lockfileExpert ? await SkillManager.fromLockfile(expertToRun, experts, {
@@ -90852,6 +90854,7 @@ var CoordinatorExecutor = class {
90852
90854
  checkpoint: await executeStateMachine({
90853
90855
  setting: {
90854
90856
  ...setting,
90857
+ model: resolvedModel,
90855
90858
  experts
90856
90859
  },
90857
90860
  initialCheckpoint,
@@ -90912,13 +90915,13 @@ var CoordinatorExecutor = class {
90912
90915
  }
90913
90916
  };
90914
90917
  }
90915
- emitInitEvent(setting, expertToRun, experts) {
90918
+ emitInitEvent(setting, resolvedModel, expertToRun, experts) {
90916
90919
  if (!this.options.eventListener) return;
90917
90920
  const initEvent = createRuntimeEvent("initializeRuntime", setting.jobId, setting.runId, {
90918
90921
  runtimeVersion: version$1,
90919
90922
  expertName: expertToRun.name,
90920
90923
  experts: Object.keys(experts),
90921
- model: setting.model,
90924
+ model: resolvedModel,
90922
90925
  maxRetries: setting.maxRetries,
90923
90926
  timeout: setting.timeout,
90924
90927
  query: setting.input.text,
@@ -91373,7 +91376,6 @@ function getAllEventContentsForJob(jobId) {
91373
91376
  //#endregion
91374
91377
  //#region ../../packages/tui/src/lib/context.ts
91375
91378
  const defaultProvider = "anthropic";
91376
- const defaultModel = "claude-sonnet-4-5";
91377
91379
  async function resolveRunContext(input) {
91378
91380
  const perstackConfig = input.perstackConfig;
91379
91381
  let checkpoint;
@@ -91386,7 +91388,7 @@ async function resolveRunContext(input) {
91386
91388
  if (checkpoint && input.expertKey && checkpoint.expert.key !== input.expertKey) throw new PerstackError(`Checkpoint expert key ${checkpoint.expert.key} does not match input expert key ${input.expertKey}`);
91387
91389
  const env = getEnv(input.envPath && input.envPath.length > 0 ? input.envPath : perstackConfig.envPath ?? [".env", ".env.local"]);
91388
91390
  const provider = input.provider ?? perstackConfig.provider?.providerName ?? defaultProvider;
91389
- const model = input.model ?? perstackConfig.model ?? defaultModel;
91391
+ const model = input.model ?? perstackConfig.model;
91390
91392
  const providerConfig = getProviderConfig(provider, env, perstackConfig.provider);
91391
91393
  const experts = Object.fromEntries(Object.entries(perstackConfig.experts ?? {}).map(([name, expert]) => {
91392
91394
  return [name, {
@@ -91460,14 +91462,6 @@ async function runHandler(expertKey, query, options, handlerOptions) {
91460
91462
  resumeFrom: input.options.resumeFrom,
91461
91463
  expertKey: input.expertKey
91462
91464
  });
91463
- let resolvedModel = model;
91464
- if (!input.options.model) {
91465
- const expertConfig = perstackConfig.experts?.[input.expertKey];
91466
- if (expertConfig?.defaultModelTier) {
91467
- const tierModel = resolveModelTier(providerConfig.providerName, expertConfig.defaultModelTier);
91468
- if (tierModel) resolvedModel = tierModel;
91469
- }
91470
- }
91471
91465
  if (handlerOptions?.additionalEnv) Object.assign(env, handlerOptions.additionalEnv(env));
91472
91466
  const lockfile = handlerOptions.lockfile;
91473
91467
  await run({
@@ -91477,7 +91471,7 @@ async function runHandler(expertKey, query, options, handlerOptions) {
91477
91471
  expertKey: input.expertKey,
91478
91472
  input: input.options.interactiveToolCallResult ? parseInteractiveToolCallResultJson(input.query) ?? (checkpoint ? parseInteractiveToolCallResult(input.query, checkpoint) : { text: input.query }) : { text: input.query },
91479
91473
  experts,
91480
- model: resolvedModel,
91474
+ model,
91481
91475
  providerConfig,
91482
91476
  reasoningBudget: input.options.reasoningBudget ?? perstackConfig.reasoningBudget,
91483
91477
  maxRetries: input.options.maxRetries ?? perstackConfig.maxRetries,
@@ -117331,7 +117325,7 @@ const truncateText = (text, maxLength) => {
117331
117325
  const assertNever = (x) => {
117332
117326
  throw new Error(`Unexpected value: ${x}`);
117333
117327
  };
117334
- const formatTimestamp = (timestamp) => new Date(timestamp).toLocaleString();
117328
+ const formatTimestamp$1 = (timestamp) => new Date(timestamp).toLocaleString();
117335
117329
  const shortenPath = (fullPath, maxLength = 60) => {
117336
117330
  if (fullPath.length <= maxLength) return fullPath;
117337
117331
  const parts = fullPath.split("/");
@@ -117515,7 +117509,7 @@ const BrowsingEventDetailInput = ({ event, onBack }) => {
117515
117509
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
117516
117510
  color: colors.muted,
117517
117511
  children: "Timestamp: "
117518
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: formatTimestamp(event.timestamp) })] }),
117512
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: formatTimestamp$1(event.timestamp) })] }),
117519
117513
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
117520
117514
  color: colors.muted,
117521
117515
  children: "ID: "
@@ -117552,7 +117546,7 @@ const BrowsingEventsInput = ({ checkpoint, events, onEventSelect, onBack }) => /
117552
117546
  "] Step ",
117553
117547
  ev.stepNumber,
117554
117548
  " (",
117555
- formatTimestamp(ev.timestamp),
117549
+ formatTimestamp$1(ev.timestamp),
117556
117550
  ")"
117557
117551
  ]
117558
117552
  }, ev.id)
@@ -117572,7 +117566,8 @@ function toolToActivity(toolCall, toolResult, reasoning, meta) {
117572
117566
  expertKey: meta.expertKey,
117573
117567
  runId: meta.runId,
117574
117568
  previousActivityId: meta.previousActivityId,
117575
- delegatedBy: meta.delegatedBy
117569
+ delegatedBy: meta.delegatedBy,
117570
+ timestamp: meta.timestamp
117576
117571
  };
117577
117572
  }
117578
117573
  function createInitialActivityProcessState() {
@@ -117665,6 +117660,7 @@ function processRunEventToActivity(state, event, addActivity) {
117665
117660
  runId: event.runId,
117666
117661
  previousActivityId: runState.lastActivityId,
117667
117662
  delegatedBy: runState.delegatedBy,
117663
+ timestamp: event.timestamp,
117668
117664
  text: queryText
117669
117665
  });
117670
117666
  runState.lastActivityId = activityId;
@@ -117692,6 +117688,7 @@ function processRunEventToActivity(state, event, addActivity) {
117692
117688
  runId: event.runId,
117693
117689
  previousActivityId: runState.lastActivityId,
117694
117690
  delegatedBy: runState.delegatedBy,
117691
+ timestamp: event.timestamp,
117695
117692
  reasoning: runState.completedReasoning,
117696
117693
  error: retryEvent.reason,
117697
117694
  message: ""
@@ -117711,6 +117708,7 @@ function processRunEventToActivity(state, event, addActivity) {
117711
117708
  runId: event.runId,
117712
117709
  previousActivityId: runState.lastActivityId,
117713
117710
  delegatedBy: runState.delegatedBy,
117711
+ timestamp: event.timestamp,
117714
117712
  reasoning: runState.completedReasoning,
117715
117713
  text
117716
117714
  });
@@ -117731,6 +117729,7 @@ function processRunEventToActivity(state, event, addActivity) {
117731
117729
  runId: event.runId,
117732
117730
  previousActivityId: runState.lastActivityId,
117733
117731
  delegatedBy: runState.delegatedBy,
117732
+ timestamp: event.timestamp,
117734
117733
  errorName: errorEvent.error.name,
117735
117734
  error: errorEvent.error.message,
117736
117735
  isRetryable: errorEvent.error.isRetryable
@@ -117764,6 +117763,7 @@ function processRunEventToActivity(state, event, addActivity) {
117764
117763
  runId: event.runId,
117765
117764
  previousActivityId: runState.lastActivityId,
117766
117765
  delegatedBy: runState.delegatedBy,
117766
+ timestamp: event.timestamp,
117767
117767
  delegateExpertKey: delegation.expert.key,
117768
117768
  query: delegation.query,
117769
117769
  reasoning
@@ -117796,6 +117796,7 @@ function processRunEventToActivity(state, event, addActivity) {
117796
117796
  runId: event.runId,
117797
117797
  previousActivityId: runState.lastActivityId,
117798
117798
  delegatedBy: runState.delegatedBy,
117799
+ timestamp: event.timestamp,
117799
117800
  skillName: toolCall.skillName,
117800
117801
  toolName: toolCall.toolName,
117801
117802
  args: toolCall.args,
@@ -117825,7 +117826,8 @@ function processRunEventToActivity(state, event, addActivity) {
117825
117826
  expertKey: event.expertKey,
117826
117827
  runId: event.runId,
117827
117828
  previousActivityId: runState.lastActivityId,
117828
- delegatedBy: runState.delegatedBy
117829
+ delegatedBy: runState.delegatedBy,
117830
+ timestamp: event.timestamp
117829
117831
  });
117830
117832
  toolActivities.push(activity);
117831
117833
  runState.lastActivityId = activityId;
@@ -118030,7 +118032,7 @@ const useRuntimeInfo = (options) => {
118030
118032
  status: "initializing",
118031
118033
  runtimeVersion: options.initialConfig.runtimeVersion,
118032
118034
  expertName: options.initialExpertName,
118033
- model: options.initialConfig.model,
118035
+ model: options.initialConfig.model ?? "",
118034
118036
  maxRetries: options.initialConfig.maxRetries,
118035
118037
  timeout: options.initialConfig.timeout,
118036
118038
  activeSkills: [],
@@ -118321,7 +118323,7 @@ const BrowsingHistoryInput = ({ jobs, onJobSelect, onJobResume, onSwitchToExpert
118321
118323
  " steps (",
118322
118324
  job.jobId,
118323
118325
  ") (",
118324
- formatTimestamp(job.startedAt),
118326
+ formatTimestamp$1(job.startedAt),
118325
118327
  ")"
118326
118328
  ]
118327
118329
  }, job.jobId)
@@ -118740,26 +118742,29 @@ function getActivityProps(activityOrGroup) {
118740
118742
  return {
118741
118743
  runId: group.runId,
118742
118744
  expertKey: group.expertKey,
118743
- delegatedBy: firstActivity?.delegatedBy
118745
+ delegatedBy: firstActivity?.delegatedBy,
118746
+ timestamp: firstActivity?.timestamp ?? Date.now()
118744
118747
  };
118745
118748
  }
118746
118749
  return activityOrGroup;
118747
118750
  }
118751
+ function formatTimestamp(ts) {
118752
+ const d = new Date(ts);
118753
+ return `${d.getFullYear()}/${String(d.getMonth() + 1).padStart(2, "0")}/${String(d.getDate()).padStart(2, "0")} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}.${String(d.getMilliseconds()).padStart(3, "0")}`;
118754
+ }
118748
118755
  const ActivityLogItem = ({ activity }) => {
118749
- const { delegatedBy, expertKey } = getActivityProps(activity);
118750
- if (delegatedBy) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
118756
+ const { expertKey, delegatedBy, timestamp } = getActivityProps(activity);
118757
+ const parts = [formatTimestamp(timestamp), expertKey];
118758
+ if (delegatedBy) parts.push(`⎇ ${delegatedBy.expertKey}`);
118759
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
118751
118760
  flexDirection: "column",
118752
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
118761
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
118753
118762
  dimColor: true,
118754
118763
  bold: true,
118755
- children: [
118756
- "[",
118757
- expertKey,
118758
- "]"
118759
- ]
118764
+ wrap: "truncate",
118765
+ children: parts.join(", ")
118760
118766
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckpointActionRow, { action: activity })]
118761
118767
  });
118762
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckpointActionRow, { action: activity });
118763
118768
  };
118764
118769
 
118765
118770
  //#endregion
@@ -119695,14 +119700,6 @@ async function startHandler(expertKey, query, options, handlerOptions) {
119695
119700
  console.error(`Checkpoint expert key ${currentCheckpoint.expert.key} does not match input expert key ${selection.expertKey}`);
119696
119701
  return;
119697
119702
  }
119698
- let resolvedModel = model;
119699
- if (!input.options.model) {
119700
- const expertConfig = perstackConfig.experts?.[selection.expertKey];
119701
- if (expertConfig?.defaultModelTier) {
119702
- const tierModel = resolveModelTier(providerConfig.providerName, expertConfig.defaultModelTier);
119703
- if (tierModel) resolvedModel = tierModel;
119704
- }
119705
- }
119706
119703
  const lockfile = handlerOptions.lockfile;
119707
119704
  let currentQuery = input.query ?? null;
119708
119705
  let currentJobId = currentCheckpoint?.jobId ?? input.options.jobId ?? createId();
@@ -119717,7 +119714,7 @@ async function startHandler(expertKey, query, options, handlerOptions) {
119717
119714
  query: currentQuery ?? void 0,
119718
119715
  config: {
119719
119716
  runtimeVersion,
119720
- model: resolvedModel,
119717
+ model,
119721
119718
  maxRetries,
119722
119719
  timeout,
119723
119720
  contextWindowUsage: currentCheckpoint?.contextWindowUsage ?? 0
@@ -119732,7 +119729,7 @@ async function startHandler(expertKey, query, options, handlerOptions) {
119732
119729
  expertKey: selection.expertKey,
119733
119730
  input: isNextQueryInteractiveToolResult && currentCheckpoint ? parseInteractiveToolCallResult(resolvedQuery, currentCheckpoint) : { text: resolvedQuery },
119734
119731
  experts,
119735
- model: resolvedModel,
119732
+ model,
119736
119733
  providerConfig,
119737
119734
  reasoningBudget: input.options.reasoningBudget ?? perstackConfig.reasoningBudget,
119738
119735
  maxRetries: input.options.maxRetries ?? perstackConfig.maxRetries,
@@ -119769,7 +119766,7 @@ async function startHandler(expertKey, query, options, handlerOptions) {
119769
119766
  //#endregion
119770
119767
  //#region package.json
119771
119768
  var name = "perstack";
119772
- var version = "0.0.108";
119769
+ var version = "0.0.110";
119773
119770
  var description = "PerStack CLI";
119774
119771
 
119775
119772
  //#endregion