perstack 0.0.80 → 0.0.82

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.
@@ -1,19 +1,188 @@
1
1
  import { createId } from '@paralleldrive/cuid2';
2
- import { parseWithFriendlyError, startCommandInputSchema, defaultMaxRetries, defaultTimeout, checkpointSchema, perstackConfigSchema } from '@perstack/core';
3
- import { createInitialJob, retrieveJob, storeJob, defaultRetrieveCheckpoint, defaultStoreEvent, defaultStoreCheckpoint, getAllJobs as getAllJobs$1, getCheckpointPath, getRunIdsByJobId, getEventContents, getAllRuns as getAllRuns$1, getCheckpointsByJobId as getCheckpointsByJobId$1 } from '@perstack/filesystem-storage';
2
+ import { parseWithFriendlyError, startCommandInputSchema, defaultMaxRetries, defaultTimeout, checkpointSchema, jobSchema, perstackConfigSchema, runSettingSchema } from '@perstack/core';
3
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'fs';
4
+ import { readFile, mkdir, writeFile } from 'fs/promises';
5
+ import path5 from 'path';
4
6
  import { findLockfile, loadLockfile, runtimeVersion, run } from '@perstack/runtime';
5
7
  import { Command } from 'commander';
6
8
  import dotenv from 'dotenv';
7
- import { readFile } from 'fs/promises';
8
- import path from 'path';
9
9
  import TOML from 'smol-toml';
10
- import { existsSync, readFileSync } from 'fs';
11
10
  import { render, useApp, useInput, Box, Text } from 'ink';
12
11
  import { createContext, useMemo, useReducer, useState, useEffect, useCallback, useRef, useInsertionEffect, useContext } from 'react';
13
12
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
14
13
  import { useRun } from '@perstack/react';
15
14
 
16
15
  // src/start.ts
16
+ function getJobsDir() {
17
+ return `${process.cwd()}/perstack/jobs`;
18
+ }
19
+ function getJobDir(jobId) {
20
+ return `${getJobsDir()}/${jobId}`;
21
+ }
22
+ function storeJob(job) {
23
+ const jobDir = getJobDir(job.id);
24
+ if (!existsSync(jobDir)) {
25
+ mkdirSync(jobDir, { recursive: true });
26
+ }
27
+ const jobPath = path5.resolve(jobDir, "job.json");
28
+ writeFileSync(jobPath, JSON.stringify(job, null, 2));
29
+ }
30
+ function retrieveJob(jobId) {
31
+ const jobDir = getJobDir(jobId);
32
+ const jobPath = path5.resolve(jobDir, "job.json");
33
+ if (!existsSync(jobPath)) {
34
+ return void 0;
35
+ }
36
+ const content = readFileSync(jobPath, "utf-8");
37
+ return jobSchema.parse(JSON.parse(content));
38
+ }
39
+ function getAllJobs() {
40
+ const jobsDir = getJobsDir();
41
+ if (!existsSync(jobsDir)) {
42
+ return [];
43
+ }
44
+ const jobDirNames = readdirSync(jobsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
45
+ if (jobDirNames.length === 0) {
46
+ return [];
47
+ }
48
+ const jobs = [];
49
+ for (const jobDirName of jobDirNames) {
50
+ const jobPath = path5.resolve(jobsDir, jobDirName, "job.json");
51
+ if (!existsSync(jobPath)) {
52
+ continue;
53
+ }
54
+ try {
55
+ const content = readFileSync(jobPath, "utf-8");
56
+ jobs.push(jobSchema.parse(JSON.parse(content)));
57
+ } catch {
58
+ }
59
+ }
60
+ return jobs.sort((a, b) => b.startedAt - a.startedAt);
61
+ }
62
+ function createInitialJob(jobId, expertKey, maxSteps) {
63
+ return {
64
+ id: jobId,
65
+ status: "running",
66
+ coordinatorExpertKey: expertKey,
67
+ runtimeVersion: "v1.0",
68
+ totalSteps: 0,
69
+ maxSteps,
70
+ usage: {
71
+ inputTokens: 0,
72
+ outputTokens: 0,
73
+ reasoningTokens: 0,
74
+ totalTokens: 0,
75
+ cachedInputTokens: 0
76
+ },
77
+ startedAt: Date.now()
78
+ };
79
+ }
80
+
81
+ // ../../packages/filesystem/src/checkpoint.ts
82
+ function getCheckpointDir(jobId) {
83
+ return `${getJobDir(jobId)}/checkpoints`;
84
+ }
85
+ function getCheckpointPath(jobId, checkpointId) {
86
+ return `${getCheckpointDir(jobId)}/${checkpointId}.json`;
87
+ }
88
+ async function defaultRetrieveCheckpoint(jobId, checkpointId) {
89
+ const checkpointPath = getCheckpointPath(jobId, checkpointId);
90
+ if (!existsSync(checkpointPath)) {
91
+ throw new Error(`checkpoint not found: ${checkpointId}`);
92
+ }
93
+ const checkpoint = await readFile(checkpointPath, "utf8");
94
+ return checkpointSchema.parse(JSON.parse(checkpoint));
95
+ }
96
+ async function defaultStoreCheckpoint(checkpoint) {
97
+ const { id, jobId } = checkpoint;
98
+ const checkpointDir = getCheckpointDir(jobId);
99
+ await mkdir(checkpointDir, { recursive: true });
100
+ await writeFile(getCheckpointPath(jobId, id), JSON.stringify(checkpoint));
101
+ }
102
+ function getCheckpointsByJobId(jobId) {
103
+ const checkpointDir = getCheckpointDir(jobId);
104
+ if (!existsSync(checkpointDir)) {
105
+ return [];
106
+ }
107
+ const files = readdirSync(checkpointDir).filter((file) => file.endsWith(".json"));
108
+ const checkpoints = [];
109
+ for (const file of files) {
110
+ try {
111
+ const content = readFileSync(path5.resolve(checkpointDir, file), "utf-8");
112
+ checkpoints.push(checkpointSchema.parse(JSON.parse(content)));
113
+ } catch {
114
+ }
115
+ }
116
+ return checkpoints.sort((a, b) => a.stepNumber - b.stepNumber);
117
+ }
118
+ function defaultGetRunDir(jobId, runId) {
119
+ return `${process.cwd()}/perstack/jobs/${jobId}/runs/${runId}`;
120
+ }
121
+ function getAllRuns() {
122
+ const jobsDir = getJobsDir();
123
+ if (!existsSync(jobsDir)) {
124
+ return [];
125
+ }
126
+ const jobDirNames = readdirSync(jobsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
127
+ if (jobDirNames.length === 0) {
128
+ return [];
129
+ }
130
+ const runs = [];
131
+ for (const jobDirName of jobDirNames) {
132
+ const runsDir = path5.resolve(jobsDir, jobDirName, "runs");
133
+ if (!existsSync(runsDir)) {
134
+ continue;
135
+ }
136
+ const runDirNames = readdirSync(runsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
137
+ for (const runDirName of runDirNames) {
138
+ const runSettingPath = path5.resolve(runsDir, runDirName, "run-setting.json");
139
+ if (!existsSync(runSettingPath)) {
140
+ continue;
141
+ }
142
+ try {
143
+ const content = readFileSync(runSettingPath, "utf-8");
144
+ runs.push(runSettingSchema.parse(JSON.parse(content)));
145
+ } catch {
146
+ }
147
+ }
148
+ }
149
+ return runs.sort((a, b) => b.updatedAt - a.updatedAt);
150
+ }
151
+
152
+ // ../../packages/filesystem/src/event.ts
153
+ async function defaultStoreEvent(event) {
154
+ const { timestamp, jobId, runId, stepNumber, type } = event;
155
+ const runDir = defaultGetRunDir(jobId, runId);
156
+ const eventPath = `${runDir}/event-${timestamp}-${stepNumber}-${type}.json`;
157
+ await mkdir(runDir, { recursive: true });
158
+ await writeFile(eventPath, JSON.stringify(event));
159
+ }
160
+ function getEventContents(jobId, runId, maxStepNumber) {
161
+ const runDir = defaultGetRunDir(jobId, runId);
162
+ if (!existsSync(runDir)) {
163
+ return [];
164
+ }
165
+ const eventFiles = readdirSync(runDir).filter((file) => file.startsWith("event-")).map((file) => {
166
+ const [_, timestamp, step, type] = file.split(".")[0].split("-");
167
+ return { file, timestamp: Number(timestamp), stepNumber: Number(step), type };
168
+ }).filter((e) => maxStepNumber === void 0 || e.stepNumber <= maxStepNumber).sort((a, b) => a.timestamp - b.timestamp);
169
+ const events = [];
170
+ for (const { file } of eventFiles) {
171
+ try {
172
+ const content = readFileSync(path5.resolve(runDir, file), "utf-8");
173
+ events.push(JSON.parse(content));
174
+ } catch {
175
+ }
176
+ }
177
+ return events;
178
+ }
179
+ function getRunIdsByJobId(jobId) {
180
+ const runsDir = path5.resolve(getJobDir(jobId), "runs");
181
+ if (!existsSync(runsDir)) {
182
+ return [];
183
+ }
184
+ return readdirSync(runsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
185
+ }
17
186
  function getEnv(envPath) {
18
187
  const env = Object.fromEntries(
19
188
  Object.entries(process.env).filter(([_, value]) => !!value).map(([key, value]) => [key, value])
@@ -63,23 +232,23 @@ async function findPerstackConfigString(configPath) {
63
232
  return await fetchRemoteConfig(configPath);
64
233
  }
65
234
  try {
66
- const tomlString = await readFile(path.resolve(process.cwd(), configPath), "utf-8");
235
+ const tomlString = await readFile(path5.resolve(process.cwd(), configPath), "utf-8");
67
236
  return tomlString;
68
237
  } catch {
69
238
  throw new Error(`Given config path "${configPath}" is not found`);
70
239
  }
71
240
  }
72
- return await findPerstackConfigStringRecursively(path.resolve(process.cwd()));
241
+ return await findPerstackConfigStringRecursively(path5.resolve(process.cwd()));
73
242
  }
74
243
  async function findPerstackConfigStringRecursively(cwd) {
75
244
  try {
76
- const tomlString = await readFile(path.resolve(cwd, "perstack.toml"), "utf-8");
245
+ const tomlString = await readFile(path5.resolve(cwd, "perstack.toml"), "utf-8");
77
246
  return tomlString;
78
247
  } catch {
79
- if (cwd === path.parse(cwd).root) {
248
+ if (cwd === path5.parse(cwd).root) {
80
249
  return null;
81
250
  }
82
- return await findPerstackConfigStringRecursively(path.dirname(cwd));
251
+ return await findPerstackConfigStringRecursively(path5.dirname(cwd));
83
252
  }
84
253
  }
85
254
  async function parsePerstackConfig(config) {
@@ -184,32 +353,32 @@ function getProviderConfig(provider, env, providerTable) {
184
353
  }
185
354
  }
186
355
  }
187
- function getAllJobs() {
188
- return getAllJobs$1();
356
+ function getAllJobs2() {
357
+ return getAllJobs();
189
358
  }
190
- function getAllRuns() {
191
- return getAllRuns$1();
359
+ function getAllRuns2() {
360
+ return getAllRuns();
192
361
  }
193
362
  function getMostRecentRun() {
194
- const runs = getAllRuns();
363
+ const runs = getAllRuns2();
195
364
  if (runs.length === 0) {
196
365
  throw new Error("No runs found");
197
366
  }
198
367
  return runs[0];
199
368
  }
200
- function getCheckpointsByJobId(jobId) {
201
- return getCheckpointsByJobId$1(jobId);
369
+ function getCheckpointsByJobId2(jobId) {
370
+ return getCheckpointsByJobId(jobId);
202
371
  }
203
372
  function getMostRecentCheckpoint(jobId) {
204
373
  const targetJobId = jobId ?? getMostRecentRun().jobId;
205
- const checkpoints = getCheckpointsByJobId(targetJobId);
374
+ const checkpoints = getCheckpointsByJobId2(targetJobId);
206
375
  if (checkpoints.length === 0) {
207
376
  throw new Error(`No checkpoints found for job ${targetJobId}`);
208
377
  }
209
378
  return checkpoints[checkpoints.length - 1];
210
379
  }
211
380
  function getRecentExperts(limit) {
212
- const runs = getAllRuns();
381
+ const runs = getAllRuns2();
213
382
  const expertMap = /* @__PURE__ */ new Map();
214
383
  for (const setting of runs) {
215
384
  const expertKey = setting.expertKey;
@@ -232,7 +401,7 @@ function getCheckpointById(jobId, checkpointId) {
232
401
  return checkpointSchema.parse(JSON.parse(checkpoint));
233
402
  }
234
403
  function getCheckpointsWithDetails(jobId) {
235
- return getCheckpointsByJobId(jobId).map((cp) => ({
404
+ return getCheckpointsByJobId2(jobId).map((cp) => ({
236
405
  id: cp.id,
237
406
  runId: cp.runId,
238
407
  stepNumber: cp.stepNumber,
@@ -1153,7 +1322,7 @@ function renderTodo(action, color) {
1153
1322
  ] }) });
1154
1323
  }
1155
1324
  function renderReadTextFile(action, color) {
1156
- const { path: path2, content, from, to } = action;
1325
+ const { path: path6, content, from, to } = action;
1157
1326
  const lineRange = from !== void 0 && to !== void 0 ? `#${from}-${to}` : "";
1158
1327
  const lines = content?.split("\n") ?? [];
1159
1328
  return /* @__PURE__ */ jsx(
@@ -1161,24 +1330,24 @@ function renderReadTextFile(action, color) {
1161
1330
  {
1162
1331
  indicatorColor: color,
1163
1332
  label: "Read Text File",
1164
- summary: `${shortenPath(path2)}${lineRange}`,
1333
+ summary: `${shortenPath(path6)}${lineRange}`,
1165
1334
  children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 1, children: /* @__PURE__ */ jsx(Text, { color: "white", dimColor: true, children: line }) }, `read-${idx}`)) })
1166
1335
  }
1167
1336
  );
1168
1337
  }
1169
1338
  function renderWriteTextFile(action, color) {
1170
- const { path: path2, text } = action;
1339
+ const { path: path6, text } = action;
1171
1340
  const lines = text.split("\n");
1172
- return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Write Text File", summary: shortenPath(path2), children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1341
+ return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Write Text File", summary: shortenPath(path6), children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1173
1342
  /* @__PURE__ */ jsx(Text, { color: "green", dimColor: true, children: "+" }),
1174
1343
  /* @__PURE__ */ jsx(Text, { color: "white", dimColor: true, children: line })
1175
1344
  ] }, `write-${idx}`)) }) });
1176
1345
  }
1177
1346
  function renderEditTextFile(action, color) {
1178
- const { path: path2, oldText, newText } = action;
1347
+ const { path: path6, oldText, newText } = action;
1179
1348
  const oldLines = oldText.split("\n");
1180
1349
  const newLines = newText.split("\n");
1181
- return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Edit Text File", summary: shortenPath(path2), children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1350
+ return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Edit Text File", summary: shortenPath(path6), children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1182
1351
  oldLines.map((line, idx) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1183
1352
  /* @__PURE__ */ jsx(Text, { color: "red", dimColor: true, children: "-" }),
1184
1353
  /* @__PURE__ */ jsx(Text, { color: "white", dimColor: true, children: line })
@@ -1190,18 +1359,18 @@ function renderEditTextFile(action, color) {
1190
1359
  ] }) });
1191
1360
  }
1192
1361
  function renderAppendTextFile(action, color) {
1193
- const { path: path2, text } = action;
1362
+ const { path: path6, text } = action;
1194
1363
  const lines = text.split("\n");
1195
- return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Append Text File", summary: shortenPath(path2), children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1364
+ return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Append Text File", summary: shortenPath(path6), children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1196
1365
  /* @__PURE__ */ jsx(Text, { color: "green", dimColor: true, children: "+" }),
1197
1366
  /* @__PURE__ */ jsx(Text, { color: "white", dimColor: true, children: line })
1198
1367
  ] }, `append-${idx}`)) }) });
1199
1368
  }
1200
1369
  function renderListDirectory(action, color) {
1201
- const { path: path2, items } = action;
1370
+ const { path: path6, items } = action;
1202
1371
  const itemLines = items?.map((item) => `${item.type === "directory" ? "\u{1F4C1}" : "\u{1F4C4}"} ${item.name}`) ?? [];
1203
1372
  const { visible, remaining } = summarizeOutput(itemLines, RENDER_CONSTANTS.LIST_DIR_MAX_ITEMS);
1204
- return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "List", summary: shortenPath(path2), children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1373
+ return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "List", summary: shortenPath(path6), children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1205
1374
  visible.map((line, idx) => /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncateText(line, UI_CONSTANTS.TRUNCATE_TEXT_DEFAULT) }, `dir-${idx}`)),
1206
1375
  remaining > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1207
1376
  "... +",
@@ -1849,7 +2018,7 @@ async function startHandler(expertKey, query, options, handlerOptions) {
1849
2018
  }));
1850
2019
  const recentExperts = getRecentExperts(10);
1851
2020
  const showHistory = !input.expertKey && !input.query && !checkpoint;
1852
- const historyJobs = showHistory ? getAllJobs().map((j) => ({
2021
+ const historyJobs = showHistory ? getAllJobs2().map((j) => ({
1853
2022
  jobId: j.id,
1854
2023
  status: j.status,
1855
2024
  expertKey: j.coordinatorExpertKey,
@@ -1987,6 +2156,6 @@ var startCommand = new Command().command("start").description("Start Perstack wi
1987
2156
  "Resume from a specific checkpoint (requires --continue or --continue-job)"
1988
2157
  ).option("-i, --interactive-tool-call-result", "Query is interactive tool call result").action((expertKey, query, options) => startHandler(expertKey, query, options));
1989
2158
 
1990
- export { getEnv, getPerstackConfig, parseInteractiveToolCallResult, parseInteractiveToolCallResultJson, resolveRunContext, startCommand, startHandler };
1991
- //# sourceMappingURL=chunk-7D4GKG4H.js.map
1992
- //# sourceMappingURL=chunk-7D4GKG4H.js.map
2159
+ export { createInitialJob, defaultRetrieveCheckpoint, defaultStoreCheckpoint, defaultStoreEvent, getAllJobs, getAllRuns, getCheckpointsByJobId, getEnv, getEventContents, getPerstackConfig, parseInteractiveToolCallResult, parseInteractiveToolCallResultJson, resolveRunContext, retrieveJob, startCommand, startHandler, storeJob };
2160
+ //# sourceMappingURL=chunk-FOHDMSVR.js.map
2161
+ //# sourceMappingURL=chunk-FOHDMSVR.js.map