@longtable/provider-codex 0.1.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/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @longtable/provider-codex
2
+
3
+ Codex-specific adapter logic for LongTable.
4
+
5
+ This package implements numbered checkpoint interaction and thin runtime-guidance injection rather than depending on a provider-native question widget.
6
+
7
+ ## Role
8
+
9
+ This package provides the Codex adapter used by `@longtable/cli`.
10
+
11
+ It is publishable as a library package, but the primary researcher-facing command surface is `longtable`, not a standalone wrapper executable.
12
+
13
+ ### Local build and test
14
+
15
+ From the workspace root:
16
+
17
+ ```bash
18
+ npm install
19
+ npm run typecheck
20
+ npm run build
21
+ ```
22
+
23
+ Then test the adapter directly:
24
+
25
+ ```bash
26
+ node packages/longtable-provider-codex/dist/wrapper-cli.js \
27
+ --print \
28
+ --mode explore \
29
+ --prompt "I want exploration, not a conclusion yet."
30
+ ```
31
+
32
+ ```bash
33
+ node packages/longtable-provider-codex/dist/wrapper-cli.js \
34
+ --exec \
35
+ --json \
36
+ --mode review \
37
+ --prompt "Review this claim critically."
38
+ ```
39
+
40
+ If the wrapped behavior changes, update this README in the same commit.
41
+
42
+ The wrapper reads the managed setup artifact when available and prepends a short runtime-guidance block so Codex receives:
43
+
44
+ - mode
45
+ - minimum question requirements
46
+ - closure disposition
47
+ - narrative-trace preservation rules
48
+ - human commitment stakes when relevant
49
+
50
+ For the recommended setup path, pair this package with `@longtable/setup`.
@@ -0,0 +1,17 @@
1
+ import type { SetupPersistedOutput } from "@longtable/setup";
2
+ import { createCodexRuntimeBridge } from "./index.js";
3
+ export interface CodexConfigFragment {
4
+ setupPath: string;
5
+ provider: "codex";
6
+ checkpointProtocol: "numbered";
7
+ defaultInteractionMode: SetupPersistedOutput["defaultInteractionMode"];
8
+ profileSummary: {
9
+ field: string;
10
+ careerStage: string;
11
+ experienceLevel: string;
12
+ currentProjectType: string;
13
+ };
14
+ runtimeGuidance: ReturnType<typeof createCodexRuntimeBridge>["runtimeDefaults"];
15
+ }
16
+ export declare function createCodexConfigFragment(setup: SetupPersistedOutput, setupPath: string): CodexConfigFragment;
17
+ export declare function renderCodexConfigToml(setup: SetupPersistedOutput, setupPath: string): string;
package/dist/config.js ADDED
@@ -0,0 +1,39 @@
1
+ import { createCodexRuntimeBridge } from "./index.js";
2
+ export function createCodexConfigFragment(setup, setupPath) {
3
+ const bridge = createCodexRuntimeBridge(setup);
4
+ return {
5
+ setupPath,
6
+ provider: "codex",
7
+ checkpointProtocol: bridge.checkpointProtocol,
8
+ defaultInteractionMode: bridge.defaultInteractionMode,
9
+ profileSummary: {
10
+ field: bridge.profile.field,
11
+ careerStage: bridge.profile.careerStage,
12
+ experienceLevel: bridge.profile.experienceLevel,
13
+ currentProjectType: bridge.profile.currentProjectType
14
+ },
15
+ runtimeGuidance: bridge.runtimeDefaults
16
+ };
17
+ }
18
+ export function renderCodexConfigToml(setup, setupPath) {
19
+ const fragment = createCodexConfigFragment(setup, setupPath);
20
+ return [
21
+ "[longtable]",
22
+ `provider = "${fragment.provider}"`,
23
+ `setup_path = "${fragment.setupPath}"`,
24
+ `checkpoint_protocol = "${fragment.checkpointProtocol}"`,
25
+ `default_interaction_mode = "${fragment.defaultInteractionMode}"`,
26
+ "",
27
+ "[longtable.profile]",
28
+ `field = "${fragment.profileSummary.field}"`,
29
+ `career_stage = "${fragment.profileSummary.careerStage}"`,
30
+ `experience_level = "${fragment.profileSummary.experienceLevel}"`,
31
+ `current_project_type = "${fragment.profileSummary.currentProjectType}"`,
32
+ "",
33
+ "[longtable.runtime_guidance]",
34
+ `ask_at_least_two_questions_in_explore = ${fragment.runtimeGuidance.askAtLeastTwoQuestionsInExplore}`,
35
+ `preserve_narrative_trace_in_draft = ${fragment.runtimeGuidance.preserveNarrativeTraceInDraft}`,
36
+ `require_why_may_be_wrong_in_review = ${fragment.runtimeGuidance.requireWhyMayBeWrongInReview}`,
37
+ `question_bias_compensation = "${fragment.runtimeGuidance.questionBiasCompensation}"`
38
+ ].join("\n");
39
+ }
@@ -0,0 +1,38 @@
1
+ import type { CheckpointSignal, ResearcherProfile, ResolvedCheckpointPolicy, RuntimeGuidance } from "@longtable/checkpoints";
2
+ import type { NumberedCheckpointSpec, ParsedCheckpointSelection, SetupPersistedOutput } from "@longtable/setup";
3
+ export interface CodexCheckpointContext {
4
+ profile: ResearcherProfile;
5
+ signal: CheckpointSignal;
6
+ spec: NumberedCheckpointSpec;
7
+ }
8
+ export interface CodexCheckpointResult {
9
+ policy: ResolvedCheckpointPolicy;
10
+ guidance: RuntimeGuidance;
11
+ prompt: string;
12
+ selection?: ParsedCheckpointSelection;
13
+ accepted: boolean;
14
+ reprompt?: string;
15
+ }
16
+ export interface CodexRuntimeDefaults {
17
+ askAtLeastTwoQuestionsInExplore: boolean;
18
+ preserveNarrativeTraceInDraft: boolean;
19
+ requireWhyMayBeWrongInReview: boolean;
20
+ questionBiasCompensation: "strong";
21
+ }
22
+ export interface CodexRuntimeBridge {
23
+ provider: "codex";
24
+ checkpointProtocol: "numbered";
25
+ supportsStructuredQuestions: false;
26
+ defaultInteractionMode: SetupPersistedOutput["defaultInteractionMode"];
27
+ profile: ResearcherProfile;
28
+ runtimeDefaults: CodexRuntimeDefaults;
29
+ }
30
+ export declare function normalizeResearcherProfile(profile: SetupPersistedOutput["profileSeed"]): ResearcherProfile;
31
+ export declare function renderRuntimeGuidance(guidance: RuntimeGuidance): string;
32
+ export declare function renderCheckpointPrompt(spec: NumberedCheckpointSpec, guidance?: RuntimeGuidance): string;
33
+ export declare function parseCheckpointResponse(spec: NumberedCheckpointSpec, input: string): ParsedCheckpointSelection | null;
34
+ export declare function buildReprompt(spec: NumberedCheckpointSpec, guidance?: RuntimeGuidance): string;
35
+ export declare function runBlockingCheckpoint(context: CodexCheckpointContext, input?: string): CodexCheckpointResult;
36
+ export declare function createCodexRuntimeBridge(setup: SetupPersistedOutput): CodexRuntimeBridge;
37
+ export * from "./config.js";
38
+ export * from "./wrapper.js";
package/dist/index.js ADDED
@@ -0,0 +1,105 @@
1
+ import { resolveCheckpointPolicy } from "@longtable/checkpoints";
2
+ import { resolveRuntimeGuidance } from "@longtable/checkpoints";
3
+ import { buildNumberedCheckpointPrompt, parseNumberedCheckpointResponse } from "@longtable/setup";
4
+ export function normalizeResearcherProfile(profile) {
5
+ return {
6
+ field: profile.field,
7
+ careerStage: profile.careerStage,
8
+ experienceLevel: profile.experienceLevel,
9
+ preferredCheckpointIntensity: profile.preferredCheckpointIntensity,
10
+ currentProjectType: profile.currentProjectType ?? "unspecified research task",
11
+ ...(profile.humanAuthorshipSignal ? { humanAuthorshipSignal: profile.humanAuthorshipSignal } : {}),
12
+ ...(profile.aiAutonomyPreference ? { aiAutonomyPreference: profile.aiAutonomyPreference } : {})
13
+ };
14
+ }
15
+ export function renderRuntimeGuidance(guidance) {
16
+ const lines = [
17
+ "LongTable runtime guidance",
18
+ `- mode: ${guidance.mode}`,
19
+ `- closure: ${guidance.closureDisposition}`,
20
+ `- minimum questions before closure: ${guidance.minimumQuestions}`,
21
+ `- question types: ${guidance.questionTypes.length > 0 ? guidance.questionTypes.join(", ") : "none"}`,
22
+ `- questions first: ${guidance.preferQuestionsFirst ? "yes" : "no"}`,
23
+ `- preserve narrative trace: ${guidance.preserveNarrativeTrace ? "yes" : "no"}`
24
+ ];
25
+ if (guidance.includeWhyMayBeWrong) {
26
+ lines.push("- include: why this may be wrong");
27
+ }
28
+ if (guidance.includeOpenTensions) {
29
+ lines.push("- include: open tensions");
30
+ }
31
+ if (guidance.surfaceHumanCommitment) {
32
+ lines.push("- include: why this requires explicit human commitment");
33
+ }
34
+ if (guidance.mandatoryQuestions.length > 0) {
35
+ lines.push("- ask first:");
36
+ for (const question of guidance.mandatoryQuestions) {
37
+ lines.push(` - ${question}`);
38
+ }
39
+ }
40
+ return lines.join("\n");
41
+ }
42
+ export function renderCheckpointPrompt(spec, guidance) {
43
+ const sections = [];
44
+ if (guidance) {
45
+ sections.push(renderRuntimeGuidance(guidance));
46
+ }
47
+ sections.push(buildNumberedCheckpointPrompt(spec));
48
+ return sections.join("\n\n");
49
+ }
50
+ export function parseCheckpointResponse(spec, input) {
51
+ return parseNumberedCheckpointResponse(spec, input);
52
+ }
53
+ export function buildReprompt(spec, guidance) {
54
+ return [
55
+ "Your response could not be parsed.",
56
+ renderCheckpointPrompt(spec, guidance)
57
+ ].join("\n\n");
58
+ }
59
+ export function runBlockingCheckpoint(context, input) {
60
+ const policy = resolveCheckpointPolicy(context.profile, context.signal);
61
+ const guidance = resolveRuntimeGuidance(context.profile, context.signal, policy);
62
+ const prompt = renderCheckpointPrompt(context.spec, guidance);
63
+ if (!policy.blocking) {
64
+ return {
65
+ policy,
66
+ guidance,
67
+ prompt,
68
+ accepted: false
69
+ };
70
+ }
71
+ const selection = input ? parseCheckpointResponse(context.spec, input) : null;
72
+ if (!selection) {
73
+ return {
74
+ policy,
75
+ guidance,
76
+ prompt,
77
+ accepted: false,
78
+ reprompt: buildReprompt(context.spec, guidance)
79
+ };
80
+ }
81
+ return {
82
+ policy,
83
+ guidance,
84
+ prompt,
85
+ selection,
86
+ accepted: true
87
+ };
88
+ }
89
+ export function createCodexRuntimeBridge(setup) {
90
+ return {
91
+ provider: "codex",
92
+ checkpointProtocol: "numbered",
93
+ supportsStructuredQuestions: false,
94
+ defaultInteractionMode: setup.defaultInteractionMode,
95
+ profile: normalizeResearcherProfile(setup.profileSeed),
96
+ runtimeDefaults: {
97
+ askAtLeastTwoQuestionsInExplore: true,
98
+ preserveNarrativeTraceInDraft: true,
99
+ requireWhyMayBeWrongInReview: true,
100
+ questionBiasCompensation: "strong"
101
+ }
102
+ };
103
+ }
104
+ export * from "./config.js";
105
+ export * from "./wrapper.js";
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { cwd, exit } from "node:process";
4
+ import { buildCodexThinWrappedPrompt, runCodexThinWrapper } from "./wrapper.js";
5
+ const VALID_MODES = new Set([
6
+ "explore",
7
+ "review",
8
+ "critique",
9
+ "draft",
10
+ "commit",
11
+ "submit"
12
+ ]);
13
+ const VALID_STAGES = new Set([
14
+ "problem_framing",
15
+ "theory_selection",
16
+ "method_design",
17
+ "measurement_design",
18
+ "analysis_planning",
19
+ "writing",
20
+ "submission"
21
+ ]);
22
+ function usage() {
23
+ return [
24
+ "Usage:",
25
+ " longtable-codex-wrapper --print --prompt \"...\" [--mode explore] [--stage problem_framing]",
26
+ " longtable-codex-wrapper --exec --prompt \"...\" [--mode review] [--json]",
27
+ "",
28
+ "Options:",
29
+ " --prompt <text> Prompt to wrap before sending to Codex",
30
+ " --mode <mode> explore|review|critique|draft|commit|submit",
31
+ " --stage <stage> problem_framing|theory_selection|method_design|measurement_design|analysis_planning|writing|submission",
32
+ " --setup <path> Override managed setup path",
33
+ " --cwd <path> Working directory to pass to codex exec",
34
+ " --print Print the wrapped prompt only",
35
+ " --exec Run codex exec with the wrapped prompt",
36
+ " --json When used with --exec, request JSONL events from Codex",
37
+ " --help Show this message"
38
+ ].join("\n");
39
+ }
40
+ function parseArgs(argv) {
41
+ const parsed = {
42
+ workingDirectory: cwd(),
43
+ json: false,
44
+ print: false,
45
+ exec: false
46
+ };
47
+ for (let index = 0; index < argv.length; index += 1) {
48
+ const token = argv[index];
49
+ switch (token) {
50
+ case "--prompt":
51
+ parsed.prompt = argv[index + 1];
52
+ index += 1;
53
+ break;
54
+ case "--mode":
55
+ if (!VALID_MODES.has(argv[index + 1])) {
56
+ throw new Error(`Invalid mode: ${argv[index + 1]}`);
57
+ }
58
+ parsed.mode = argv[index + 1];
59
+ index += 1;
60
+ break;
61
+ case "--stage":
62
+ if (!VALID_STAGES.has(argv[index + 1])) {
63
+ throw new Error(`Invalid stage: ${argv[index + 1]}`);
64
+ }
65
+ parsed.stage = argv[index + 1];
66
+ index += 1;
67
+ break;
68
+ case "--setup":
69
+ parsed.setupPath = argv[index + 1];
70
+ index += 1;
71
+ break;
72
+ case "--cwd":
73
+ parsed.workingDirectory = argv[index + 1];
74
+ index += 1;
75
+ break;
76
+ case "--json":
77
+ parsed.json = true;
78
+ break;
79
+ case "--print":
80
+ parsed.print = true;
81
+ break;
82
+ case "--exec":
83
+ parsed.exec = true;
84
+ break;
85
+ case "--help":
86
+ console.log(usage());
87
+ exit(0);
88
+ default:
89
+ throw new Error(`Unknown argument: ${token}`);
90
+ }
91
+ }
92
+ if (!parsed.print && !parsed.exec) {
93
+ parsed.print = true;
94
+ }
95
+ return parsed;
96
+ }
97
+ function resolvePrompt(prompt) {
98
+ if (prompt && prompt.trim()) {
99
+ return prompt.trim();
100
+ }
101
+ if (!process.stdin.isTTY) {
102
+ return readFileSync(0, "utf8").trim();
103
+ }
104
+ return "";
105
+ }
106
+ async function main() {
107
+ const args = parseArgs(process.argv.slice(2));
108
+ const prompt = resolvePrompt(args.prompt);
109
+ if (!prompt) {
110
+ throw new Error("A prompt is required. Pass --prompt or pipe text on stdin.");
111
+ }
112
+ if (args.print) {
113
+ const wrapped = await buildCodexThinWrappedPrompt({
114
+ prompt,
115
+ mode: args.mode,
116
+ researchStage: args.stage,
117
+ setupPath: args.setupPath,
118
+ workingDirectory: args.workingDirectory
119
+ });
120
+ console.log(wrapped.wrappedPrompt);
121
+ return;
122
+ }
123
+ const exitCode = await runCodexThinWrapper({
124
+ prompt,
125
+ mode: args.mode,
126
+ researchStage: args.stage,
127
+ setupPath: args.setupPath,
128
+ workingDirectory: args.workingDirectory,
129
+ json: args.json
130
+ });
131
+ exit(exitCode);
132
+ }
133
+ main().catch((error) => {
134
+ console.error(error instanceof Error ? error.message : String(error));
135
+ console.error("");
136
+ console.error(usage());
137
+ exit(1);
138
+ });
@@ -0,0 +1,20 @@
1
+ import type { InteractionMode, ResearchStage, RuntimeGuidance } from "@longtable/core";
2
+ export interface CodexThinWrapperOptions {
3
+ prompt: string;
4
+ mode?: InteractionMode;
5
+ researchStage?: ResearchStage;
6
+ workingDirectory?: string;
7
+ setupPath?: string;
8
+ }
9
+ export interface CodexWrappedPrompt {
10
+ mode: InteractionMode;
11
+ researchStage: ResearchStage;
12
+ guidance: RuntimeGuidance;
13
+ wrappedPrompt: string;
14
+ setupLoaded: boolean;
15
+ }
16
+ export declare function buildCodexThinWrappedPrompt(options: CodexThinWrapperOptions): Promise<CodexWrappedPrompt>;
17
+ export interface CodexThinWrapperExecOptions extends CodexThinWrapperOptions {
18
+ json?: boolean;
19
+ }
20
+ export declare function runCodexThinWrapper(options: CodexThinWrapperExecOptions): Promise<number>;
@@ -0,0 +1,141 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { spawnSync } from "node:child_process";
3
+ import { cwd } from "node:process";
4
+ import { resolveCheckpointPolicy, resolveRuntimeGuidance } from "@longtable/checkpoints";
5
+ import { loadSetupOutput, resolveDefaultSetupPath } from "@longtable/setup";
6
+ import { normalizeResearcherProfile, renderRuntimeGuidance } from "./index.js";
7
+ function defaultProfile() {
8
+ return {
9
+ field: "unknown",
10
+ careerStage: "unknown",
11
+ experienceLevel: "intermediate",
12
+ preferredCheckpointIntensity: "balanced",
13
+ currentProjectType: "unspecified research task"
14
+ };
15
+ }
16
+ function defaultMode(setup) {
17
+ return setup?.defaultInteractionMode ?? "explore";
18
+ }
19
+ function defaultStage(mode) {
20
+ switch (mode) {
21
+ case "draft":
22
+ return "writing";
23
+ case "commit":
24
+ return "theory_selection";
25
+ case "submit":
26
+ return "submission";
27
+ case "review":
28
+ case "critique":
29
+ return "analysis_planning";
30
+ case "explore":
31
+ default:
32
+ return "problem_framing";
33
+ }
34
+ }
35
+ function defaultCheckpointLevel(mode) {
36
+ switch (mode) {
37
+ case "submit":
38
+ return "adaptive_required";
39
+ case "commit":
40
+ return "recommended";
41
+ case "review":
42
+ case "critique":
43
+ case "draft":
44
+ case "explore":
45
+ default:
46
+ return "recommended";
47
+ }
48
+ }
49
+ async function loadManagedSetup(customPath) {
50
+ const target = resolveDefaultSetupPath(customPath).path;
51
+ if (!existsSync(target)) {
52
+ return null;
53
+ }
54
+ try {
55
+ return await loadSetupOutput(customPath);
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ }
61
+ function readPromptFromStdin() {
62
+ if (process.stdin.isTTY) {
63
+ return "";
64
+ }
65
+ return readFileSync(0, "utf8").trim();
66
+ }
67
+ export async function buildCodexThinWrappedPrompt(options) {
68
+ const setup = await loadManagedSetup(options.setupPath);
69
+ const profile = setup ? normalizeResearcherProfile(setup.profileSeed) : defaultProfile();
70
+ const mode = options.mode ?? defaultMode(setup ?? undefined);
71
+ const researchStage = options.researchStage ?? defaultStage(mode);
72
+ const prompt = options.prompt.trim();
73
+ const signal = {
74
+ checkpointKey: "runtime_guidance",
75
+ baseLevel: defaultCheckpointLevel(mode),
76
+ mode,
77
+ artifactStakes: mode === "submit"
78
+ ? "external_submission"
79
+ : mode === "commit"
80
+ ? "study_protocol"
81
+ : mode === "draft"
82
+ ? "internal_draft"
83
+ : "private_note",
84
+ researchStage,
85
+ unresolvedTensions: setup?.initialState.openTensions ?? [],
86
+ studyContract: setup?.initialState.studyContract
87
+ };
88
+ const policy = resolveCheckpointPolicy(profile, signal);
89
+ const guidance = resolveRuntimeGuidance(profile, signal, policy);
90
+ const sections = [renderRuntimeGuidance(guidance)];
91
+ const instructionLines = [
92
+ "LongTable ordering rules",
93
+ guidance.minimumQuestions > 0 || guidance.mustAskBeforeClosure
94
+ ? "- surface the mandatory questions before any recommendation or closure"
95
+ : "- keep the response aligned to the stated mode",
96
+ guidance.includeWhyMayBeWrong ? "- include why this may be wrong before synthesis" : undefined,
97
+ guidance.includeOpenTensions ? "- keep unresolved tensions visible" : undefined,
98
+ guidance.preserveNarrativeTrace ? "- preserve narrative trace and avoid generic fluency" : undefined,
99
+ guidance.surfaceHumanCommitment ? "- make the human commitment stakes explicit before closing" : undefined,
100
+ profile.humanAuthorshipSignal
101
+ ? `- preserve this human authorship signal: ${profile.humanAuthorshipSignal}`
102
+ : undefined
103
+ ].filter(Boolean);
104
+ sections.push(instructionLines.join("\n"));
105
+ sections.push(["User prompt", prompt].join("\n"));
106
+ return {
107
+ mode,
108
+ researchStage,
109
+ guidance,
110
+ wrappedPrompt: sections.join("\n\n"),
111
+ setupLoaded: Boolean(setup)
112
+ };
113
+ }
114
+ export async function runCodexThinWrapper(options) {
115
+ const prompt = options.prompt.trim() || readPromptFromStdin();
116
+ if (!prompt) {
117
+ throw new Error("A prompt is required. Pass --prompt or pipe text on stdin.");
118
+ }
119
+ const wrapped = await buildCodexThinWrappedPrompt({
120
+ ...options,
121
+ prompt
122
+ });
123
+ const args = [
124
+ "exec",
125
+ "--color",
126
+ "never",
127
+ "-s",
128
+ "read-only",
129
+ "-C",
130
+ options.workingDirectory ?? cwd(),
131
+ "--skip-git-repo-check"
132
+ ];
133
+ if (options.json) {
134
+ args.push("--json");
135
+ }
136
+ args.push(wrapped.wrappedPrompt);
137
+ const result = spawnSync("codex", args, {
138
+ stdio: "inherit"
139
+ });
140
+ return result.status ?? 1;
141
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@longtable/provider-codex",
3
+ "version": "0.1.9",
4
+ "private": false,
5
+ "description": "Codex adapter surface for LongTable",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.json",
15
+ "typecheck": "tsc -p tsconfig.json --noEmit"
16
+ },
17
+ "dependencies": {
18
+ "@longtable/checkpoints": "0.1.9",
19
+ "@longtable/core": "0.1.9",
20
+ "@longtable/setup": "0.1.9"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.10.1",
24
+ "typescript": "^5.6.0"
25
+ },
26
+ "keywords": [
27
+ "longtable",
28
+ "research",
29
+ "codex",
30
+ "wrapper",
31
+ "checkpoints"
32
+ ],
33
+ "author": "Hosung You",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/HosungYou/LongTable.git"
38
+ },
39
+ "homepage": "https://github.com/HosungYou/LongTable#readme",
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "engines": {
44
+ "node": ">=18.0.0"
45
+ }
46
+ }