oh-my-opencode-slim 0.9.7 → 0.9.9
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 +2 -1
- package/dist/config/schema.d.ts +2 -0
- package/dist/index.js +63 -15
- package/dist/interview/server.d.ts +2 -0
- package/oh-my-opencode-slim.schema.json +6 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -240,7 +240,8 @@ var BackgroundTaskConfigSchema = z2.object({
|
|
|
240
240
|
var InterviewConfigSchema = z2.object({
|
|
241
241
|
maxQuestions: z2.number().int().min(1).max(10).default(2),
|
|
242
242
|
outputFolder: z2.string().min(1).default("interview"),
|
|
243
|
-
autoOpenBrowser: z2.boolean().default(true)
|
|
243
|
+
autoOpenBrowser: z2.boolean().default(true),
|
|
244
|
+
port: z2.number().int().min(0).max(65535).default(0)
|
|
244
245
|
});
|
|
245
246
|
var TodoContinuationConfigSchema = z2.object({
|
|
246
247
|
maxContinuations: z2.number().int().min(1).max(50).default(5).describe("Maximum consecutive auto-continuations before stopping to ask user"),
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -150,6 +150,7 @@ export declare const InterviewConfigSchema: z.ZodObject<{
|
|
|
150
150
|
maxQuestions: z.ZodDefault<z.ZodNumber>;
|
|
151
151
|
outputFolder: z.ZodDefault<z.ZodString>;
|
|
152
152
|
autoOpenBrowser: z.ZodDefault<z.ZodBoolean>;
|
|
153
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
153
154
|
}, z.core.$strip>;
|
|
154
155
|
export type InterviewConfig = z.infer<typeof InterviewConfigSchema>;
|
|
155
156
|
export declare const TodoContinuationConfigSchema: z.ZodObject<{
|
|
@@ -282,6 +283,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
282
283
|
maxQuestions: z.ZodDefault<z.ZodNumber>;
|
|
283
284
|
outputFolder: z.ZodDefault<z.ZodString>;
|
|
284
285
|
autoOpenBrowser: z.ZodDefault<z.ZodBoolean>;
|
|
286
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
285
287
|
}, z.core.$strip>>;
|
|
286
288
|
todoContinuation: z.ZodOptional<z.ZodObject<{
|
|
287
289
|
maxContinuations: z.ZodDefault<z.ZodNumber>;
|
package/dist/index.js
CHANGED
|
@@ -534,7 +534,8 @@ var BackgroundTaskConfigSchema = z2.object({
|
|
|
534
534
|
var InterviewConfigSchema = z2.object({
|
|
535
535
|
maxQuestions: z2.number().int().min(1).max(10).default(2),
|
|
536
536
|
outputFolder: z2.string().min(1).default("interview"),
|
|
537
|
-
autoOpenBrowser: z2.boolean().default(true)
|
|
537
|
+
autoOpenBrowser: z2.boolean().default(true),
|
|
538
|
+
port: z2.number().int().min(0).max(65535).default(0)
|
|
538
539
|
});
|
|
539
540
|
var TodoContinuationConfigSchema = z2.object({
|
|
540
541
|
maxContinuations: z2.number().int().min(1).max(50).default(5).describe("Maximum consecutive auto-continuations before stopping to ask user"),
|
|
@@ -4528,7 +4529,7 @@ function renderInterviewPage(interviewId) {
|
|
|
4528
4529
|
</div>
|
|
4529
4530
|
|
|
4530
4531
|
<script>
|
|
4531
|
-
const interviewId = ${JSON.stringify(interviewId)};
|
|
4532
|
+
const interviewId = ${JSON.stringify(interviewId).replace(/</g, "\\u003c")};
|
|
4532
4533
|
const state = { data: null, answers: {}, activeQuestionIndex: 0, lastSig: null, customMode: {} };
|
|
4533
4534
|
|
|
4534
4535
|
function updateSubmitButton() {
|
|
@@ -4885,12 +4886,17 @@ function renderInterviewPage(interviewId) {
|
|
|
4885
4886
|
}
|
|
4886
4887
|
});
|
|
4887
4888
|
|
|
4889
|
+
function schedulePoll() {
|
|
4890
|
+
setTimeout(async () => {
|
|
4891
|
+
try { await refresh(); } catch (_) {}
|
|
4892
|
+
if (state.data?.mode !== 'abandoned') schedulePoll();
|
|
4893
|
+
}, 2500);
|
|
4894
|
+
}
|
|
4895
|
+
|
|
4888
4896
|
refresh().catch((error) => {
|
|
4889
4897
|
document.getElementById('submitStatus').textContent = error.message || 'Failed to load interview.';
|
|
4890
4898
|
});
|
|
4891
|
-
|
|
4892
|
-
refresh().catch(() => {});
|
|
4893
|
-
}, 2500);
|
|
4899
|
+
schedulePoll();
|
|
4894
4900
|
</script>
|
|
4895
4901
|
</body>
|
|
4896
4902
|
</html>`;
|
|
@@ -4944,6 +4950,7 @@ async function readJsonBody(request) {
|
|
|
4944
4950
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
4945
4951
|
size += buffer.length;
|
|
4946
4952
|
if (size > 64 * 1024) {
|
|
4953
|
+
request.destroy();
|
|
4947
4954
|
throw new Error("Request body too large");
|
|
4948
4955
|
}
|
|
4949
4956
|
chunks.push(buffer);
|
|
@@ -4965,8 +4972,15 @@ function sendHtml(response, html) {
|
|
|
4965
4972
|
function createInterviewServer(deps) {
|
|
4966
4973
|
let baseUrl = null;
|
|
4967
4974
|
let startPromise = null;
|
|
4975
|
+
let activeServer = null;
|
|
4968
4976
|
async function handle(request, response) {
|
|
4969
|
-
|
|
4977
|
+
let url;
|
|
4978
|
+
try {
|
|
4979
|
+
url = new URL2(request.url ?? "/", "http://127.0.0.1");
|
|
4980
|
+
} catch {
|
|
4981
|
+
sendJson(response, 400, { error: "Invalid request URL" });
|
|
4982
|
+
return;
|
|
4983
|
+
}
|
|
4970
4984
|
const pathname = url.pathname;
|
|
4971
4985
|
if (request.method === "GET" && pathname.startsWith("/interview/")) {
|
|
4972
4986
|
sendHtml(response, renderInterviewPage(pathname.split("/").pop() ?? "unknown"));
|
|
@@ -4978,9 +4992,9 @@ function createInterviewServer(deps) {
|
|
|
4978
4992
|
const state = await deps.getState(stateMatch[1]);
|
|
4979
4993
|
sendJson(response, 200, state);
|
|
4980
4994
|
} catch (error) {
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
});
|
|
4995
|
+
const message = error instanceof Error ? error.message : "Interview not found";
|
|
4996
|
+
const status = message === "Interview not found" ? 404 : 500;
|
|
4997
|
+
sendJson(response, status, { error: message });
|
|
4984
4998
|
}
|
|
4985
4999
|
return;
|
|
4986
5000
|
}
|
|
@@ -5020,11 +5034,20 @@ function createInterviewServer(deps) {
|
|
|
5020
5034
|
});
|
|
5021
5035
|
});
|
|
5022
5036
|
});
|
|
5037
|
+
server.requestTimeout = 30000;
|
|
5038
|
+
server.headersTimeout = 1e4;
|
|
5039
|
+
activeServer = server;
|
|
5023
5040
|
server.on("error", (error) => {
|
|
5041
|
+
server.close();
|
|
5042
|
+
activeServer = null;
|
|
5024
5043
|
startPromise = null;
|
|
5025
|
-
|
|
5044
|
+
if (error.code === "EADDRINUSE") {
|
|
5045
|
+
reject(new Error(`Interview server port ${deps.port} is already in use. Choose a different port or set port to 0 for an OS-assigned port.`));
|
|
5046
|
+
} else {
|
|
5047
|
+
reject(error);
|
|
5048
|
+
}
|
|
5026
5049
|
});
|
|
5027
|
-
server.listen(
|
|
5050
|
+
server.listen(deps.port, "127.0.0.1", () => {
|
|
5028
5051
|
const address = server.address();
|
|
5029
5052
|
if (!address || typeof address === "string") {
|
|
5030
5053
|
startPromise = null;
|
|
@@ -5038,7 +5061,16 @@ function createInterviewServer(deps) {
|
|
|
5038
5061
|
return startPromise;
|
|
5039
5062
|
}
|
|
5040
5063
|
return {
|
|
5041
|
-
ensureStarted
|
|
5064
|
+
ensureStarted,
|
|
5065
|
+
close: () => {
|
|
5066
|
+
if (activeServer) {
|
|
5067
|
+
activeServer.closeAllConnections();
|
|
5068
|
+
activeServer.close();
|
|
5069
|
+
activeServer = null;
|
|
5070
|
+
}
|
|
5071
|
+
baseUrl = null;
|
|
5072
|
+
startPromise = null;
|
|
5073
|
+
}
|
|
5042
5074
|
};
|
|
5043
5075
|
}
|
|
5044
5076
|
|
|
@@ -5300,6 +5332,15 @@ async function ensureInterviewFile(record) {
|
|
|
5300
5332
|
}
|
|
5301
5333
|
}
|
|
5302
5334
|
async function readInterviewDocument(record) {
|
|
5335
|
+
try {
|
|
5336
|
+
return await fs5.readFile(record.markdownPath, "utf8");
|
|
5337
|
+
} catch (error) {
|
|
5338
|
+
if (error.code === "ENOENT") {
|
|
5339
|
+
try {
|
|
5340
|
+
return await fs5.readFile(record.markdownPath, "utf8");
|
|
5341
|
+
} catch {}
|
|
5342
|
+
}
|
|
5343
|
+
}
|
|
5303
5344
|
await ensureInterviewFile(record);
|
|
5304
5345
|
return fs5.readFile(record.markdownPath, "utf8");
|
|
5305
5346
|
}
|
|
@@ -5334,6 +5375,7 @@ function resolveExistingInterviewPath(directory, outputFolder, value) {
|
|
|
5334
5375
|
}
|
|
5335
5376
|
const outputDir = createInterviewDirectoryPath(directory, outputFolder);
|
|
5336
5377
|
const candidates = new Set;
|
|
5378
|
+
const resolvedRoot = path6.resolve(directory);
|
|
5337
5379
|
if (path6.isAbsolute(trimmed)) {
|
|
5338
5380
|
candidates.add(trimmed);
|
|
5339
5381
|
} else {
|
|
@@ -5347,6 +5389,10 @@ function resolveExistingInterviewPath(directory, outputFolder, value) {
|
|
|
5347
5389
|
if (path6.extname(candidate) !== ".md") {
|
|
5348
5390
|
continue;
|
|
5349
5391
|
}
|
|
5392
|
+
const resolved = path6.resolve(candidate);
|
|
5393
|
+
if (!resolved.startsWith(resolvedRoot + path6.sep) && resolved !== resolvedRoot) {
|
|
5394
|
+
continue;
|
|
5395
|
+
}
|
|
5350
5396
|
if (fsSync.existsSync(candidate)) {
|
|
5351
5397
|
return candidate;
|
|
5352
5398
|
}
|
|
@@ -5363,6 +5409,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
5363
5409
|
const sessionBusy = new Map;
|
|
5364
5410
|
const browserOpened = new Set;
|
|
5365
5411
|
let resolveBaseUrl = null;
|
|
5412
|
+
let idCounter = 0;
|
|
5366
5413
|
function setBaseUrlResolver(resolver) {
|
|
5367
5414
|
resolveBaseUrl = resolver;
|
|
5368
5415
|
}
|
|
@@ -5439,7 +5486,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
5439
5486
|
}
|
|
5440
5487
|
const messages = await loadMessages(sessionID);
|
|
5441
5488
|
const record = {
|
|
5442
|
-
id: `${Date.now()}-${slugify(idea) || "interview"}`,
|
|
5489
|
+
id: `${Date.now()}-${++idCounter}-${slugify(idea) || "interview"}`,
|
|
5443
5490
|
sessionID,
|
|
5444
5491
|
idea: normalizedIdea,
|
|
5445
5492
|
markdownPath: createInterviewFilePath(ctx.directory, outputFolder, idea),
|
|
@@ -5467,7 +5514,7 @@ function createInterviewService(ctx, config, deps) {
|
|
|
5467
5514
|
const messages = await loadMessages(sessionID);
|
|
5468
5515
|
const title = extractTitle(document);
|
|
5469
5516
|
const record = {
|
|
5470
|
-
id: `${Date.now()}-${slugify(path6.basename(markdownPath, ".md")) || "interview"}`,
|
|
5517
|
+
id: `${Date.now()}-${++idCounter}-${slugify(path6.basename(markdownPath, ".md")) || "interview"}`,
|
|
5471
5518
|
sessionID,
|
|
5472
5519
|
idea: title || path6.basename(markdownPath, ".md"),
|
|
5473
5520
|
markdownPath,
|
|
@@ -5668,7 +5715,8 @@ function createInterviewManager(ctx, config) {
|
|
|
5668
5715
|
const service = createInterviewService(ctx, config.interview);
|
|
5669
5716
|
const server = createInterviewServer({
|
|
5670
5717
|
getState: async (interviewId) => service.getInterviewState(interviewId),
|
|
5671
|
-
submitAnswers: async (interviewId, answers) => service.submitAnswers(interviewId, answers)
|
|
5718
|
+
submitAnswers: async (interviewId, answers) => service.submitAnswers(interviewId, answers),
|
|
5719
|
+
port: config.interview?.port ?? 0
|
|
5672
5720
|
});
|
|
5673
5721
|
service.setBaseUrlResolver(() => server.ensureStarted());
|
|
5674
5722
|
return {
|
|
@@ -2,6 +2,8 @@ import type { InterviewAnswer, InterviewState } from './types';
|
|
|
2
2
|
export declare function createInterviewServer(deps: {
|
|
3
3
|
getState: (interviewId: string) => Promise<InterviewState>;
|
|
4
4
|
submitAnswers: (interviewId: string, answers: InterviewAnswer[]) => Promise<void>;
|
|
5
|
+
port: number;
|
|
5
6
|
}): {
|
|
6
7
|
ensureStarted: () => Promise<string>;
|
|
8
|
+
close: () => void;
|
|
7
9
|
};
|
package/package.json
CHANGED