apiskill 0.1.4 → 0.1.6
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 +11 -1
- package/README.zh.md +13 -1
- package/dist/assets/index-B_wAJLu_.js +324 -0
- package/dist/assets/index-CHOQ6xhP.css +1 -0
- package/dist/index.html +2 -2
- package/docs/cli.md +16 -2
- package/docs/cli.zh.md +16 -2
- package/docs/mcp.ja.md +2 -4
- package/docs/mcp.ko.md +2 -4
- package/docs/mcp.md +2 -4
- package/docs/mcp.zh.md +2 -4
- package/docs/web.md +3 -1
- package/docs/web.zh.md +3 -1
- package/mcp-config.example.json +1 -5
- package/package.json +4 -3
- package/scripts/apiskill-cli.mjs +48 -9
- package/scripts/lib/apiskill-core.mjs +6 -0
- package/scripts/lib/cache-paths.mjs +94 -4
- package/scripts/test-shared-cache.mjs +175 -0
- package/src/App.tsx +29 -0
- package/src/CacheSettingsDialog.tsx +166 -0
- package/src/DocumentVersionManager.tsx +47 -1
- package/src/styles.css +119 -0
- package/src/swagger.ts +2 -0
- package/vite.config.ts +92 -1
- package/dist/assets/index-CV6e3HAP.js +0 -299
- package/dist/assets/index-slFOragU.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{color-scheme:light;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;background:#eef1f4;color:#18202a;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased}*{box-sizing:border-box}body{margin:0;min-width:320px;min-height:100vh}button,input,select{font:inherit}button{cursor:pointer}.app-shell{display:flex;flex-direction:column;height:100svh;min-height:100vh;padding:18px 18px 0;overflow:hidden}.topbar{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:16px 20px;border:1px solid #d7dde4;border-radius:8px;background:#fff}.eyebrow{margin:0 0 4px;color:#667282;font-size:12px;font-weight:700;letter-spacing:0;text-transform:uppercase}h1,h2,h3,h4,p{margin-top:0}.topbar h1{margin:0;font-size:24px;line-height:1.2}.topbar-actions{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:14px;min-width:0}.version-switcher{display:inline-flex;align-items:center;gap:8px;color:#647182;font-size:12px;font-weight:800}.version-switcher select{width:min(320px,32vw);min-height:36px;padding:0 10px;border:1px solid #cfd7e1;border-radius:6px;outline:0;color:#15202c;background:#fff}.version-manager{min-width:min(420px,38vw)}.version-manager-trigger{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:9px;width:100%;min-height:44px;padding:7px 10px;border:1px solid #cfd7e1;border-radius:7px;color:#23303e;background:#fff;text-align:left}.version-manager-trigger:disabled{cursor:not-allowed;opacity:.72}.version-manager-trigger span{display:grid;gap:2px;min-width:0}.version-manager-trigger strong,.version-manager-trigger small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.version-manager-trigger strong{font-size:13px}.version-manager-trigger small{color:#657181;font-size:11px;font-weight:800}.version-manager-dialog{width:min(900px,100%);max-height:calc(100vh - 40px);overflow:auto;border-radius:8px;border:1px solid #d7dde4;background:#fff;box-shadow:0 18px 60px #12192338}.version-manager-body{display:grid;gap:12px;padding:16px 20px 20px;background:#f7f9fb}.version-message{padding:9px 11px;border:1px solid #cbe5df;border-radius:7px;color:#176b4f;background:#eef9f5;font-size:13px;font-weight:800}.version-message.error{border-color:#f1c1bd;color:#9a2d27;background:#fff4f3}.version-item{display:grid;gap:12px;padding:13px;border:1px solid #dfe5eb;border-radius:8px;background:#fff}.version-item.active{border-color:#7ab6ad;box-shadow:inset 3px 0 #1b6b65}.version-item-main{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:start}.version-item-title{display:flex;align-items:center;gap:8px;min-width:0}.version-item-title strong{overflow:hidden;color:#202a36;text-overflow:ellipsis;white-space:nowrap}.version-item-title span{padding:3px 7px;border-radius:999px;color:#176b4f;background:#e5f6ef;font-size:11px;font-weight:900}.version-item-main p{margin:4px 0 0;overflow-wrap:anywhere;color:#657181;font-family:SFMono-Regular,Consolas,monospace;font-size:12px}.version-item-stats{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px}.version-item-stats span,.version-env-summary span{padding:5px 7px;border-radius:6px;color:#4d5b6c;background:#eef2f6;font-size:12px;font-weight:800}.version-env-summary{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:8px}.version-env-summary code{overflow:hidden;padding:7px 9px;border:1px solid #dfe5eb;border-radius:6px;color:#2f3d4d;background:#fbfcfd;text-overflow:ellipsis;white-space:nowrap}.version-env-editor{display:grid;grid-template-columns:minmax(160px,.5fr) minmax(0,1fr);gap:10px}.version-env-editor label{display:grid;gap:5px;min-width:0;color:#647182;font-size:12px;font-weight:800}.version-env-editor input{width:100%;min-height:36px;padding:0 10px;border:1px solid #cfd7e1;border-radius:6px;outline:0;color:#15202c;background:#fff}.version-item-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}.sync-meta{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;color:#5c6877;font-size:13px}.sync-meta span{padding:5px 8px;border:1px solid #dce2e9;border-radius:6px;background:#f7f9fb}.primary-button,.copy-inline-button{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:36px;border:0;border-radius:6px;color:#fff;background:#1b6b65;font-weight:700;white-space:nowrap}.primary-button{padding:0 14px}.primary-button:disabled{cursor:wait;opacity:.72}.copy-inline-button{padding:0 12px;background:#202a36}.secondary-button{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:36px;padding:0 14px;border:1px solid #ccd5df;border-radius:6px;color:#22303d;background:#fff;font-weight:700;white-space:nowrap}.secondary-button:disabled{cursor:wait;opacity:.72}.danger-button{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:36px;padding:0 14px;border:1px solid #efcbc8;border-radius:6px;color:#b63831;background:#fff7f6;font-weight:700;white-space:nowrap}.icon-secondary-button{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;border:1px solid #ccd5df;border-radius:6px;color:#22303d;background:#fff}.source-panel{display:grid;gap:10px;margin-top:12px;padding:12px;border:1px solid #d7dde4;border-radius:8px;background:#fff}.source-panel-header{display:flex;align-items:center;justify-content:space-between;gap:10px}.source-panel-actions{display:inline-flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}.document-toolbar{grid-template-columns:minmax(0,1fr) auto;align-items:center}.document-source-summary{display:grid;min-width:0;gap:3px}.document-source-summary>span{color:#6a7685;font-size:12px;font-weight:700}.document-source-summary strong,.document-source-summary small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.document-source-summary strong{color:#1d2936;font-size:13px}.document-source-summary small{color:#176b4f;font-size:12px}.document-toolbar>.version-message{grid-column:1 / -1}.source-tabs{display:inline-flex;width:fit-content;gap:4px;padding:3px;border:1px solid #d4dce5;border-radius:7px;background:#fff}.source-tabs button{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;padding:0 12px;border:0;border-radius:5px;color:#566373;background:transparent;font-size:13px;font-weight:800}.source-tabs button.active{color:#fff;background:#1b6b65}.source-form{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:10px}.document-dialog{width:min(760px,100%);max-height:calc(100vh - 40px);overflow:auto;border:1px solid #d7dde4;border-radius:8px;background:#fff;box-shadow:0 18px 60px #12192338;animation:dialog-enter .16s ease-out}.document-dialog-tabs{display:flex;gap:2px;overflow-x:auto;padding:10px 20px 0;border-bottom:1px solid #e5eaf0}.document-dialog-tabs button{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;gap:6px;min-height:38px;padding:0 12px;border:0;border-bottom:2px solid transparent;color:#5c6877;background:transparent;font-size:13px;font-weight:800}.document-dialog-tabs button.active{border-bottom-color:#1b6b65;color:#155a55}.document-dialog-body{min-height:174px;padding:20px;background:#f7f9fb}.blank-document-fields{display:grid;gap:12px}.document-source-input{min-height:46px;background:#fff}.document-file-picker{display:grid;grid-template-columns:auto minmax(0,1fr);align-content:center;min-height:134px;padding:22px;background:#fff}.document-file-picker>svg{grid-row:1 / span 2}.document-file-picker small{color:#748091}.document-dialog-actions{gap:8px;padding-top:18px}.document-dialog-actions .clear-document-source{margin-right:auto}.source-input,.source-file-picker{display:flex;align-items:center;gap:8px;min-width:0;min-height:38px;padding:0 12px;border:1px solid #cfd7e1;border-radius:7px;background:#f9fafb}.source-file-picker{cursor:pointer}.source-file-picker input{display:none}.source-file-picker span{overflow:hidden;min-width:0;color:#15202c;text-overflow:ellipsis;white-space:nowrap}.source-input input{width:100%;min-width:0;border:0;outline:0;background:transparent;color:#15202c}.source-input textarea{width:100%;min-width:0;min-height:118px;resize:vertical;border:0;outline:0;background:transparent;color:#15202c;font-family:SFMono-Regular,Consolas,monospace;font-size:12px;line-height:1.5}.source-input-multiline{align-items:flex-start;padding-top:10px;padding-bottom:10px}.source-input-multiline svg{margin-top:2px}.source-auth{grid-column:1 / -1;display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:10px}.source-auth-fields{display:grid;grid-template-columns:repeat(2,minmax(0,220px));gap:8px}.source-auth-fields label{display:flex;align-items:center;gap:8px;min-width:0;min-height:36px;padding:0 10px;border:1px solid #cfd7e1;border-radius:7px;background:#f9fafb}.source-auth-fields input{width:100%;min-width:0;border:0;outline:0;background:transparent;color:#15202c}.source-meta{grid-column:1 / -1;overflow:hidden;color:#657181;font-size:12px;text-overflow:ellipsis;white-space:nowrap}.source-meta span{color:#2f3d4d;font-family:SFMono-Regular,Consolas,monospace}.source-meta small{margin-left:8px;color:#728094;font-weight:800}.workspace{display:grid;flex:1 1 auto;grid-template-columns:minmax(320px,380px) minmax(0,1fr);gap:16px;min-height:0;margin-top:16px}.sidebar,.detail-pane{min-height:0;border:1px solid #d7dde4;border-radius:8px;background:#fff}.sidebar{display:flex;flex-direction:column;padding:14px}.search-box{display:flex;align-items:center;gap:8px;min-height:42px;padding:0 12px;border:1px solid #cfd7e1;border-radius:7px;background:#f9fafb}.search-box input{width:100%;min-width:0;border:0;outline:0;background:transparent;color:#15202c}.search-clear-button{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:24px;height:24px;padding:0;border:0;border-radius:5px;color:#647182;background:transparent}.search-clear-button:hover{color:#b63831;background:#fff0ef}.filters{display:grid;grid-template-columns:1fr auto;gap:10px;margin-top:12px}.filters label:first-child{display:flex;align-items:center;gap:8px;min-width:0;padding:0 10px;border:1px solid #d5dce5;border-radius:7px;background:#fff}.filters select{width:100%;min-height:38px;border:0;outline:0;background:transparent}.checkbox-line{display:inline-flex;align-items:center;gap:6px;color:#566373;font-size:13px;white-space:nowrap}.method-tabs{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:6px;margin-top:12px}.method-tabs button{min-height:32px;border:1px solid #d5dce5;border-radius:6px;color:#536171;background:#fff;font-size:12px;font-weight:800}.method-tabs button.active{color:#fff;border-color:#1b6b65;background:#1b6b65}.endpoint-count{margin:12px 2px 8px;color:#6b7582;font-size:13px}.endpoint-list{flex:1 1 auto;min-height:0;overflow:auto;display:flex;flex-direction:column;gap:6px;padding-right:4px}.endpoint-item{display:grid;grid-template-columns:64px minmax(0,1fr);align-items:start;gap:10px;width:100%;min-height:66px;padding:10px;border:1px solid transparent;border-radius:7px;text-align:left;background:#fff}.endpoint-item:hover{background:#f6f8fa}.endpoint-item.selected{border-color:#1b6b65;background:#edf7f5}.endpoint-main{min-width:0}.endpoint-main strong,.endpoint-main small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.endpoint-main strong{margin-bottom:4px;color:#1d2732;font-size:14px}.endpoint-main small{color:#647182;font-family:SFMono-Regular,Consolas,monospace;font-size:12px}.method{display:inline-flex;align-items:center;justify-content:center;width:58px;height:24px;border-radius:5px;color:#fff;font-size:11px;font-weight:900}.method-get{background:#246bce}.method-post{background:#168456}.method-put{background:#9a6200}.method-delete{background:#c23131}.method-patch{background:#7953b8}.method-options,.method-head{background:#52606f}.detail-pane{overflow:auto}.opened-api-tabs{position:sticky;top:0;z-index:5;display:flex;align-items:flex-end;gap:4px;overflow-x:auto;overflow-y:hidden;min-height:47px;padding:10px 12px 0;border-bottom:1px solid #dfe5eb;background:#f7f9fb}.opened-api-tab{--tab-accent: #52606f;--tab-soft: #eef2f6;display:inline-grid;grid-template-columns:auto minmax(100px,210px) 20px;align-items:center;gap:7px;min-width:180px;max-width:280px;height:36px;padding:7px 8px 8px;border:1px solid color-mix(in srgb,var(--tab-accent) 45%,#d5dce5);border-bottom-color:#dfe5eb;border-radius:7px 7px 0 0;text-align:left;background:var(--tab-soft);cursor:pointer}.opened-api-tab-get{--tab-accent: #246bce;--tab-soft: #eef5ff}.opened-api-tab-post{--tab-accent: #168456;--tab-soft: #edf8f2}.opened-api-tab-put{--tab-accent: #9a6200;--tab-soft: #fff5e4}.opened-api-tab-delete{--tab-accent: #c23131;--tab-soft: #fff0f0}.opened-api-tab-patch{--tab-accent: #7953b8;--tab-soft: #f4efff}.opened-api-tab-options,.opened-api-tab-head{--tab-accent: #52606f;--tab-soft: #f0f3f6}.opened-api-tab.active{position:relative;margin-bottom:-1px;border-color:var(--tab-accent);border-bottom-color:#fff;background:#fff;box-shadow:inset 0 3px 0 var(--tab-accent)}.opened-api-tab-text{min-width:0}.opened-api-tab-text strong{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.opened-api-tab-text strong{color:#1d2732;font-size:12px;font-weight:800}.opened-api-tab-method{display:inline-flex;align-items:center;justify-content:center;min-width:34px;height:18px;padding:0 5px;border-radius:4px;color:#fff;font-size:9px;font-weight:900;line-height:1}.opened-api-tab-close{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:0;border-radius:5px;color:var(--tab-accent);background:transparent;cursor:pointer}.opened-api-tab-close:hover{color:#b63831;background:#fff0ef}.endpoint-detail{padding:22px}.detail-header{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;padding-bottom:18px;border-bottom:1px solid #e1e6ec}.detail-actions{display:flex;align-items:center;gap:8px;margin-left:auto}.endpoint-title-line{display:flex;align-items:center;gap:10px}.endpoint-title-line h2{margin:0;color:#121923;font-size:22px;line-height:1.25}.path-row{display:flex;align-items:center;gap:8px;min-width:0;margin-top:10px}.path-line{display:block;min-width:0;color:#354253;font-size:13px;white-space:normal;word-break:break-all}.path-copy-button{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;gap:4px;min-height:28px;padding:0 8px;border:1px solid #d4dce5;border-radius:6px;color:#415166;background:#fff;font-size:12px;font-weight:800}.path-copy-button:hover{border-color:#9fb0c1;background:#f7f9fb}.tag-row{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}.tag-row span{padding:4px 8px;border-radius:5px;color:#475364;background:#edf1f5;font-size:12px;font-weight:700}.description{margin:16px 0 0;color:#4f5c6a;line-height:1.7}.copy-preview{margin-top:18px;border:1px solid #d9e0e7;border-radius:8px;background:#f8fafb}.copy-preview-toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px;border-bottom:1px solid #dfe5eb}.copy-tabs{display:inline-flex;gap:4px;padding:3px;border:1px solid #d4dce5;border-radius:7px;background:#fff}.copy-tabs button{display:inline-flex;align-items:center;gap:6px;min-height:30px;padding:0 10px;border:0;border-radius:5px;color:#566373;background:transparent;font-size:13px;font-weight:800}.copy-tabs button.active{color:#fff;background:#1b6b65}.copy-preview-content{overflow:auto;min-height:calc(3 * 1.6em + 28px);max-height:calc(25.6em + 28px);margin:0;padding:14px;color:#e5edf6;background:#111827;font-family:SFMono-Regular,Consolas,monospace;font-size:12px;line-height:1.6;white-space:pre-wrap;overflow-wrap:anywhere}.request-tester{display:grid;gap:14px;padding:14px;background:#fff}.endpoint-links-config{display:grid;gap:12px;padding:14px;background:#fff}.links-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.links-actions,.link-editor-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}.link-editor-panel{min-width:0}.link-button{text-decoration:none}.request-line{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:end;gap:10px}.request-actions{display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}.request-line label,.tester-form-grid label,.param-editor label{display:grid;gap:5px;min-width:0;color:#647182;font-size:12px;font-weight:800}.request-line input,.tester-form-grid input,.tester-form-grid select,.param-editor input{width:100%;min-height:36px;padding:0 10px;border:1px solid #cfd7e1;border-radius:6px;outline:0;color:#15202c;background:#fff}.request-preview{display:block;overflow:hidden;padding:9px 10px;border:1px solid #dfe5eb;border-radius:6px;color:#334254;background:#f7f9fb;font-size:12px;text-overflow:ellipsis;white-space:nowrap}.tester-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.tester-panel{min-width:0;padding:12px;border:1px solid #dfe5eb;border-radius:7px;background:#fbfcfd}.tester-panel-title{display:flex;align-items:center;gap:6px;margin-bottom:10px;color:#243242;font-size:13px;font-weight:900}.tester-form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.param-editor+.param-editor{margin-top:12px}.param-editor h4{margin:0 0 8px;color:#516070;font-size:12px}.param-editor label+label{margin-top:8px}.param-editor span{display:inline-flex;align-items:center;gap:3px}.param-editor b{color:#c23131}.tester-panel textarea{width:100%;min-height:168px;resize:vertical;padding:10px;border:1px solid #cfd7e1;border-radius:6px;outline:0;color:#1f2b38;background:#fff;font-family:SFMono-Regular,Consolas,monospace;font-size:12px;line-height:1.5}.tester-panel textarea:disabled{color:#8792a1;background:#f2f5f8}.response-panel{background:#fff}.response-meta-line{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px}.response-meta-line span{padding:5px 8px;border-radius:5px;color:#536171;background:#eef2f6;font-size:12px;font-weight:800}.response-meta-line .status-ok{color:#176b4f;background:#e5f6ef}.response-meta-line .status-error{color:#a8322b;background:#fff0ef}.response-panel pre{overflow:auto;max-height:360px;margin:0;padding:12px;border-radius:6px;color:#202a36;background:#f7f9fb;font-family:SFMono-Regular,Consolas,monospace;font-size:12px;line-height:1.55}.detail-section{padding:20px 0;border-bottom:1px solid #e8edf2}.section-title{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}.section-title h3{margin:0;font-size:16px}.section-title span{min-width:28px;padding:3px 8px;border-radius:999px;color:#1b6b65;background:#e7f4f2;text-align:center;font-size:12px;font-weight:800}.media-block+.media-block,.response-block+.response-block{margin-top:18px}.media-block h4{margin:0 0 8px;color:#516070;font-family:SFMono-Regular,Consolas,monospace;font-size:13px}.response-heading{display:flex;align-items:center;gap:10px;margin-bottom:10px}.response-heading strong{padding:4px 8px;border-radius:5px;color:#fff;background:#202a36;font-size:12px}.response-heading span{color:#657181;font-size:13px}.table-wrap{overflow:auto;border:1px solid #dfe5eb;border-radius:7px}table{width:100%;min-width:920px;border-collapse:collapse;font-size:13px}th,td{padding:10px 12px;border-bottom:1px solid #edf1f4;text-align:left;vertical-align:top}th{position:sticky;top:0;z-index:1;color:#526171;background:#f7f9fb;font-size:12px;font-weight:800}tr:last-child td{border-bottom:0}td code{color:#17202b;font-family:SFMono-Regular,Consolas,monospace;font-size:12px}.muted{margin:0;color:#798596}.inline-error{display:flex;align-items:center;gap:8px;margin-top:12px;padding:10px 12px;border:1px solid #f1c1bd;border-radius:7px;color:#9a2d27;background:#fff4f3}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:30;display:grid;place-items:center;padding:20px;background:#1219237a;animation:backdrop-enter .14s ease-out}.import-dialog{width:min(720px,100%);max-height:calc(100vh - 40px);overflow:auto;border-radius:8px;border:1px solid #d7dde4;background:#fff;box-shadow:0 18px 60px #12192338}.auth-dialog{width:min(560px,100%);max-height:calc(100vh - 40px);overflow:auto;border-radius:8px;border:1px solid #d7dde4;background:#fff;box-shadow:0 18px 60px #12192338}.cache-settings-dialog{width:min(680px,100%);max-height:calc(100vh - 40px);overflow:auto;border:1px solid #d7dde4;border-radius:8px;background:#fff;box-shadow:0 18px 60px #12192338}.cache-settings-body{display:grid;gap:16px;padding:18px 20px}.cache-location-row,.cache-settings-details>div{display:grid;grid-template-columns:150px minmax(0,1fr);align-items:start;gap:14px}.cache-location-row>span,.cache-settings-details dt{color:#657181;font-size:12px;font-weight:800}.cache-location-row code,.cache-settings-details dd{min-width:0;margin:0;overflow-wrap:anywhere;color:#1d2a36;font-size:12px}.cache-settings-field{display:grid;gap:7px;color:#334152;font-size:12px;font-weight:800}.cache-settings-field input{width:100%;min-height:40px;padding:0 11px;border:1px solid #bdc8d4;border-radius:6px;outline:0;color:#15202c;background:#fff}.cache-settings-field input:focus{border-color:#1b6b65;box-shadow:0 0 0 3px #1b6b651f}.cache-settings-notice{display:grid;grid-template-columns:auto minmax(0,1fr);gap:10px;padding:12px 0;border-block:1px solid #dbe7e4;color:#285c55}.cache-settings-notice.warning{border-color:#ead9ad;color:#765b16}.cache-settings-notice strong{font-size:13px}.cache-settings-notice p{margin:4px 0 0;color:#657181;font-size:12px;line-height:1.55}.cache-settings-details{display:grid;gap:9px;margin:0}.cache-settings-actions{justify-content:space-between}.cache-settings-actions>div{display:flex;gap:8px}.api-config-dialog{width:80vw;min-width:1000px;max-width:80vw;max-height:calc(100vh - 40px);overflow:auto;border-radius:8px;border:1px solid #d7dde4;background:#fff;box-shadow:0 18px 60px #12192338}.import-dialog-header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:18px 20px 14px;border-bottom:1px solid #e5eaf0}.import-dialog-header h2{margin:0;font-size:20px}.import-badge{display:inline-flex;align-items:center;min-height:28px;padding:0 10px;border-radius:999px;font-size:12px;font-weight:800;white-space:nowrap}.import-badge.running{color:#8a5b00;background:#fff4d8}.import-badge.success{color:#176b4f;background:#e5f6ef}.import-badge.error{color:#a8322b;background:#fff0ef}.progress-track{height:8px;margin:16px 20px 0;overflow:hidden;border-radius:999px;background:#e9eef3}.progress-bar{height:100%;border-radius:inherit;background:#1b6b65;transition:width .24s ease}.progress-bar.running{background:linear-gradient(90deg,#1b6b65,#4f8fcb)}.progress-bar.success{background:#168456}.progress-bar.error{background:#c23131}.import-summary{display:grid;gap:12px;padding:18px 20px}.auth-dialog-body{display:grid;gap:14px;padding:18px 20px}.api-config-body{display:grid;gap:14px;padding:18px 20px;background:#f7f9fb}.api-config-tabs{display:inline-flex;width:fit-content;gap:4px;padding:3px;border:1px solid #d4dce5;border-radius:7px;background:#fff}.api-config-tabs button{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;padding:0 12px;border:0;border-radius:5px;color:#566373;background:transparent;font-size:13px;font-weight:800}.api-config-tabs button.active{color:#fff;background:#1b6b65}.api-config-errors{display:flex;align-items:flex-start;gap:10px;padding:10px 12px;border:1px solid #efcbc8;border-radius:7px;color:#8f2f29;background:#fff7f6}.api-config-errors strong{display:block;margin-bottom:4px;font-size:13px}.api-config-errors p{margin:0;font-size:12px;line-height:1.45}.api-cli-editor{width:100%;min-height:calc(4.65em + 24px);max-height:calc(24.8em + 24px);overflow-y:auto;resize:vertical;padding:12px;border:1px solid #334155;border-radius:7px;outline:0;color:#e5edf6;background:#111827;font-family:SFMono-Regular,Consolas,monospace;font-size:12px;line-height:1.55}.api-cli-editor::placeholder{color:#94a3b8}.api-cli-editor:focus{border-color:#5aa69d;box-shadow:0 0 0 3px #5aa69d38}.api-config-section{display:grid;gap:12px;padding:14px;border:1px solid #dfe5eb;border-radius:8px;background:#fff}.section-title-row{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.section-title-row h3{margin:0;color:#243242;font-size:15px}.section-title-row p{margin:4px 0 0;color:#657181;font-size:12px}.section-title-row code{max-width:420px;overflow:hidden;padding:5px 7px;border:1px solid #dce2e9;border-radius:6px;color:#415166;background:#f7f9fb;text-overflow:ellipsis;white-space:nowrap}.api-config-grid{display:grid;grid-template-columns:140px repeat(2,minmax(0,1fr));gap:10px}.api-config-grid.compact{grid-template-columns:repeat(3,minmax(0,1fr)) auto}.api-config-grid label,.field-config-row label{display:grid;gap:5px;min-width:0;color:#647182;font-size:12px;font-weight:800}.api-config-grid input,.api-config-grid select,.api-config-grid textarea,.field-config-row input,.field-config-row select{width:100%;min-height:36px;padding:0 10px;border:1px solid #cfd7e1;border-radius:6px;outline:0;color:#15202c;background:#fff}.api-config-grid textarea{min-height:82px;padding:10px;resize:vertical;line-height:1.45}.span-2{grid-column:span 2}.span-all{grid-column:1 / -1}.inline-check{display:inline-flex;align-items:center;gap:6px;min-height:32px;color:#415166;font-size:13px;font-weight:800}.field-config-table{display:grid;gap:8px;overflow-x:auto}.field-config-item{display:grid;gap:8px}.field-config-head,.field-config-row{display:grid;grid-template-columns:minmax(150px,1.1fr) 120px 58px minmax(180px,1.2fr) minmax(120px,.8fr) 38px;gap:8px;align-items:center;min-width:760px}.field-config-head.with-location,.field-config-row.with-location{grid-template-columns:98px minmax(150px,1.1fr) 120px 58px minmax(180px,1.2fr) minmax(120px,.8fr) 38px}.field-config-head{color:#657181;font-size:12px;font-weight:900}.field-check{place-items:center}.field-check input,.inline-check input{width:16px;min-height:16px}.icon-danger-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border:1px solid #efcbc8;border-radius:6px;color:#b63831;background:#fff7f6}.field-add-row{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:34px;border:1px dashed #b8c4d0;border-radius:6px;color:#415166;background:#fbfcfd;font-weight:800}.child-field-config{display:grid;gap:8px;margin-left:18px;padding:10px;border-left:3px solid #b9d7d3;border-radius:0 7px 7px 0;background:#f7fbfa}.child-field-title{color:#415166;font-size:12px;font-weight:900}.response-config{display:grid;gap:10px;padding:12px;border:1px solid #e3e8ee;border-radius:7px;background:#fbfcfd}.auth-message{display:flex;align-items:flex-start;gap:8px;padding:10px 12px;border:1px solid #d9e0e7;border-radius:7px;color:#2f3d4d;background:#f7f9fb}.auth-target{display:grid;gap:4px}.auth-target span,.auth-fields label{color:#657181;font-size:12px;font-weight:800}.auth-target strong{overflow-wrap:anywhere;color:#1f2b38;font-family:SFMono-Regular,Consolas,monospace;font-size:13px;line-height:1.5}.auth-fields{display:grid;gap:10px}.auth-fields label{display:grid;gap:6px}.auth-fields input{width:100%;min-height:38px;padding:0 10px;border:1px solid #cfd7e1;border-radius:6px;outline:0;color:#15202c;background:#fff}.import-summary div:not(.import-counts):not(.import-error){display:grid;gap:4px}.import-summary span{color:#657181;font-size:12px;font-weight:700}.import-summary strong{overflow-wrap:anywhere;color:#1f2b38;font-family:SFMono-Regular,Consolas,monospace;font-size:13px;line-height:1.5}.import-counts{display:flex;flex-wrap:wrap;gap:8px}.import-counts span{padding:6px 9px;border-radius:6px;color:#1b6b65;background:#e7f4f2}.import-error{display:flex;align-items:flex-start;gap:8px;padding:10px 12px;border:1px solid #f1c1bd;border-radius:7px;color:#9a2d27;background:#fff4f3}.import-dialog-actions{display:flex;justify-content:flex-end;padding:0 20px 18px}.state-screen,.empty-state{display:grid;place-items:center;align-content:center;min-height:100vh;padding:24px;text-align:center}.empty-state{min-height:60vh}.state-icon{width:42px;height:42px;margin-bottom:14px;color:#1b6b65}.state-icon.error{color:#b8312d}.state-screen h1,.empty-state h2{margin-bottom:8px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@keyframes backdrop-enter{0%{opacity:0}}@keyframes dialog-enter{0%{opacity:0;transform:translateY(8px)}}@media(max-width:980px){.app-shell{height:auto;padding:12px;overflow:visible}.topbar{align-items:flex-start;flex-direction:column}.topbar-actions{width:100%;justify-content:space-between}.version-switcher,.version-switcher select{width:100%}.version-manager{width:100%;min-width:0}.version-item-main,.version-env-editor,.version-env-summary{grid-template-columns:1fr}.version-item-stats,.version-item-actions{justify-content:flex-start}.source-panel{grid-template-columns:1fr}.source-panel-header{align-items:stretch;flex-direction:column}.source-panel-header>.secondary-button,.source-panel-actions,.source-tabs{width:100%}.source-panel-actions{justify-content:stretch}.source-panel-actions .secondary-button{flex:1 1 180px}.source-tabs{flex-wrap:wrap}.document-dialog-tabs{width:100%}.source-form,.source-auth,.source-auth-fields,.api-config-grid,.api-config-grid.compact{grid-template-columns:1fr}.span-2,.span-all{grid-column:auto}.workspace{grid-template-columns:1fr;flex:none;height:auto}.sidebar{max-height:46vh}.detail-pane{min-height:60vh}.detail-header{flex-direction:column}.copy-preview-toolbar{align-items:stretch;flex-direction:column}.copy-tabs,.copy-inline-button{width:100%}.copy-tabs button{flex:1;justify-content:center}.request-line,.tester-grid,.links-grid,.tester-form-grid{grid-template-columns:1fr}}@media(max-width:640px){.filters{grid-template-columns:1fr}.method-tabs{grid-template-columns:repeat(3,minmax(0,1fr))}.topbar-actions,.sync-meta{align-items:stretch;flex-direction:column;width:100%}.primary-button,.secondary-button,.copy-inline-button,.source-tabs{width:100%}.source-tabs button{flex:1}.document-dialog{max-height:calc(100vh - 20px)}.document-dialog-tabs{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));overflow:visible;padding-inline:12px}.document-dialog-tabs button{min-width:0;padding-inline:6px}.document-dialog-body{min-height:210px;padding:16px}.document-dialog-actions .clear-document-source{margin-right:0}.import-dialog-actions .secondary-button{width:100%}.import-dialog-actions{flex-direction:column-reverse;gap:8px}.cache-location-row,.cache-settings-details>div{grid-template-columns:1fr;gap:5px}.cache-settings-actions,.cache-settings-actions>div{display:grid;width:100%}.endpoint-title-line{align-items:flex-start;flex-direction:column}}
|
package/dist/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>API Skill Console</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-B_wAJLu_.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CHOQ6xhP.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/docs/cli.md
CHANGED
|
@@ -14,11 +14,12 @@ apiskill --help
|
|
|
14
14
|
apiskill run web
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
`apiskill run web
|
|
17
|
+
`apiskill run web`, CLI commands, and MCP all use `~/.apiskill/cache` by default. `--cwd` changes only the Web working directory and does not change the shared cache. For project isolation, set the same absolute `APISKILL_CACHE_DIR` for every Web, CLI, and MCP process:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
apiskill run web --port 8890
|
|
21
|
-
apiskill run web --cwd /path/to/project
|
|
21
|
+
APISKILL_CACHE_DIR=/path/to/project/.apiskill-cache apiskill run web --cwd /path/to/project
|
|
22
|
+
APISKILL_CACHE_DIR=/path/to/project/.apiskill-cache apiskill check --json
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
From the source project root:
|
|
@@ -34,6 +35,19 @@ You can also run the executable directly:
|
|
|
34
35
|
node scripts/apiskill-cli.mjs --help
|
|
35
36
|
```
|
|
36
37
|
|
|
38
|
+
## Cache Settings
|
|
39
|
+
|
|
40
|
+
Web, CLI, and MCP share the saved default in `~/.apiskill/config.json`:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
apiskill cache show
|
|
44
|
+
apiskill cache show --json
|
|
45
|
+
apiskill cache set /absolute/path/to/apiskill-cache
|
|
46
|
+
apiskill cache reset
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Changing the default never moves or deletes existing files. Future CLI and MCP processes use the new location immediately; restart Web or use Update Cache Location in version management to copy selected versions. `APISKILL_CACHE_DIR` has priority over the saved setting.
|
|
50
|
+
|
|
37
51
|
## Import Documents
|
|
38
52
|
|
|
39
53
|
Check whether a usable cache is available:
|
package/docs/cli.zh.md
CHANGED
|
@@ -14,11 +14,12 @@ apiskill --help
|
|
|
14
14
|
apiskill run web
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
`apiskill run web
|
|
17
|
+
`apiskill run web`、其他 CLI 命令和 MCP 默认统一使用 `~/.apiskill/cache`。`--cwd` 只改变 Web 工作目录,不会改变共享缓存。需要按项目隔离时,必须为 Web、CLI 和 MCP 设置相同的绝对路径 `APISKILL_CACHE_DIR`:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
20
|
apiskill run web --port 8890
|
|
21
|
-
apiskill run web --cwd /path/to/project
|
|
21
|
+
APISKILL_CACHE_DIR=/path/to/project/.apiskill-cache apiskill run web --cwd /path/to/project
|
|
22
|
+
APISKILL_CACHE_DIR=/path/to/project/.apiskill-cache apiskill check --json
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
在源码项目根目录运行:
|
|
@@ -34,6 +35,19 @@ npm run cli -- --help
|
|
|
34
35
|
node scripts/apiskill-cli.mjs --help
|
|
35
36
|
```
|
|
36
37
|
|
|
38
|
+
## 缓存设置
|
|
39
|
+
|
|
40
|
+
Web、CLI 和 MCP 共用保存在 `~/.apiskill/config.json` 中的默认缓存地址:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
apiskill cache show
|
|
44
|
+
apiskill cache show --json
|
|
45
|
+
apiskill cache set /absolute/path/to/apiskill-cache
|
|
46
|
+
apiskill cache reset
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
修改默认地址不会移动或删除已有缓存文件。后续 CLI、MCP 进程会直接使用新地址,Web 需要重启;也可以在 Web 版本管理中选择“更新缓存地址”,将需要的版本复制到新目录。`APISKILL_CACHE_DIR` 环境变量优先于保存的默认设置。
|
|
50
|
+
|
|
37
51
|
## 导入文档
|
|
38
52
|
|
|
39
53
|
先检查当前是否有可用缓存:
|
package/docs/mcp.ja.md
CHANGED
|
@@ -20,12 +20,10 @@ cwd = "/Users/dobby/dev/apiskill"
|
|
|
20
20
|
startup_timeout_sec = 10
|
|
21
21
|
tool_timeout_sec = 60
|
|
22
22
|
enabled = true
|
|
23
|
-
|
|
24
|
-
[mcp_servers.apiskill.env]
|
|
25
|
-
APISKILL_ROOT = "/Users/dobby/dev/apiskill"
|
|
26
|
-
APISKILL_CACHE_DIR = "/Users/dobby/dev/apiskill/cache"
|
|
27
23
|
```
|
|
28
24
|
|
|
25
|
+
キャッシュ環境変数を設定しない場合、MCP、Web、CLI は同じ `~/.apiskill/cache` を使用します。プロジェクトごとに分離する場合は、3 つのプロセスすべてに同じ絶対パスの `APISKILL_CACHE_DIR` を設定してください。
|
|
26
|
+
|
|
29
27
|
Codex を再起動して `/mcp` を実行します。`apiskill` と以下のツールが表示されるはずです。
|
|
30
28
|
|
|
31
29
|
## 読み取りツール
|
package/docs/mcp.ko.md
CHANGED
|
@@ -20,12 +20,10 @@ cwd = "/Users/dobby/dev/apiskill"
|
|
|
20
20
|
startup_timeout_sec = 10
|
|
21
21
|
tool_timeout_sec = 60
|
|
22
22
|
enabled = true
|
|
23
|
-
|
|
24
|
-
[mcp_servers.apiskill.env]
|
|
25
|
-
APISKILL_ROOT = "/Users/dobby/dev/apiskill"
|
|
26
|
-
APISKILL_CACHE_DIR = "/Users/dobby/dev/apiskill/cache"
|
|
27
23
|
```
|
|
28
24
|
|
|
25
|
+
캐시 환경 변수를 설정하지 않으면 MCP, Web, CLI는 모두 같은 `~/.apiskill/cache`를 사용합니다. 프로젝트별로 분리할 때는 세 프로세스에 동일한 절대 경로의 `APISKILL_CACHE_DIR`를 설정하세요.
|
|
26
|
+
|
|
29
27
|
Codex를 재시작하고 `/mcp`를 실행합니다. `apiskill`과 아래 도구들이 보여야 합니다.
|
|
30
28
|
|
|
31
29
|
## 읽기 도구
|
package/docs/mcp.md
CHANGED
|
@@ -22,12 +22,10 @@ cwd = "/Users/dobby/dev/apiskill"
|
|
|
22
22
|
startup_timeout_sec = 10
|
|
23
23
|
tool_timeout_sec = 60
|
|
24
24
|
enabled = true
|
|
25
|
-
|
|
26
|
-
[mcp_servers.apiskill.env]
|
|
27
|
-
APISKILL_ROOT = "/Users/dobby/dev/apiskill"
|
|
28
|
-
APISKILL_CACHE_DIR = "/Users/dobby/dev/apiskill/cache"
|
|
29
25
|
```
|
|
30
26
|
|
|
27
|
+
With no cache environment configured, MCP uses the same `~/.apiskill/cache` default as Web and CLI. For project isolation, set one absolute `APISKILL_CACHE_DIR` value and use that exact value for all three processes.
|
|
28
|
+
|
|
31
29
|
Restart Codex and run `/mcp`. You should see `apiskill` with the tools below.
|
|
32
30
|
|
|
33
31
|
## Read Tools
|
package/docs/mcp.zh.md
CHANGED
|
@@ -22,12 +22,10 @@ cwd = "/Users/dobby/dev/apiskill"
|
|
|
22
22
|
startup_timeout_sec = 10
|
|
23
23
|
tool_timeout_sec = 60
|
|
24
24
|
enabled = true
|
|
25
|
-
|
|
26
|
-
[mcp_servers.apiskill.env]
|
|
27
|
-
APISKILL_ROOT = "/Users/dobby/dev/apiskill"
|
|
28
|
-
APISKILL_CACHE_DIR = "/Users/dobby/dev/apiskill/cache"
|
|
29
25
|
```
|
|
30
26
|
|
|
27
|
+
不配置缓存环境变量时,MCP 与 Web、CLI 一样默认使用 `~/.apiskill/cache`。需要按项目隔离时,设置一个绝对路径 `APISKILL_CACHE_DIR`,并确保三个进程使用完全相同的值。
|
|
28
|
+
|
|
31
29
|
重启 Codex 后运行 `/mcp`,应该能看到 `apiskill` 和下面的工具。
|
|
32
30
|
|
|
33
31
|
## 只读工具
|
package/docs/web.md
CHANGED
|
@@ -11,7 +11,7 @@ npm install -g apiskill
|
|
|
11
11
|
apiskill run web
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Web, CLI, and MCP use the same user-writable `~/.apiskill/cache` directory by default. `--cwd` changes only the working directory. For an isolated cache, set the same absolute `APISKILL_CACHE_DIR` for all three processes. When developing this repository, you can also use:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
npm install
|
|
@@ -20,6 +20,8 @@ npm run dev
|
|
|
20
20
|
|
|
21
21
|
Open the local URL printed by the terminal.
|
|
22
22
|
|
|
23
|
+
Cache Settings in the upper-right corner shows the active session location and changes the saved default. Existing files are never moved or deleted. Restart Web to use the new location; Update Cache Location on a version shows the source and destination before copying while retaining the source file.
|
|
24
|
+
|
|
23
25
|
## Import Sources
|
|
24
26
|
|
|
25
27
|
The web app supports four import paths:
|
package/docs/web.zh.md
CHANGED
|
@@ -11,7 +11,7 @@ npm install -g apiskill
|
|
|
11
11
|
apiskill run web
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Web、CLI 和 MCP 默认统一使用用户可写的 `~/.apiskill/cache`,共享同一份文档和版本。`--cwd` 只改变工作目录。需要隔离缓存时,必须为三个进程设置相同的绝对路径 `APISKILL_CACHE_DIR`。开发本仓库时也可以使用:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
npm install
|
|
@@ -20,6 +20,8 @@ npm run dev
|
|
|
20
20
|
|
|
21
21
|
打开终端输出的本地地址。
|
|
22
22
|
|
|
23
|
+
页面右上角“缓存设置”用于查看当前会话位置和修改默认缓存地址。修改设置不会移动或删除已有文件;Web 重启后使用新地址。版本管理中每个版本的“更新缓存地址”会先展示当前位置和目标位置,确认后复制该版本,并保留源文件。
|
|
24
|
+
|
|
23
25
|
## 导入来源
|
|
24
26
|
|
|
25
27
|
“新建文档”弹窗通过 Tab 支持空白文档和四种导入方式:
|
package/mcp-config.example.json
CHANGED
|
@@ -3,11 +3,7 @@
|
|
|
3
3
|
"apiskill": {
|
|
4
4
|
"command": "node",
|
|
5
5
|
"args": ["/Users/dobby/dev/apiskill/scripts/mcp-server.mjs"],
|
|
6
|
-
"cwd": "/Users/dobby/dev/apiskill"
|
|
7
|
-
"env": {
|
|
8
|
-
"APISKILL_ROOT": "/Users/dobby/dev/apiskill",
|
|
9
|
-
"APISKILL_CACHE_DIR": "/Users/dobby/dev/apiskill/cache"
|
|
10
|
-
}
|
|
6
|
+
"cwd": "/Users/dobby/dev/apiskill"
|
|
11
7
|
}
|
|
12
8
|
}
|
|
13
9
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apiskill",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"dev": "vite --host 0.0.0.0 && node scripts/mcp-server.mjs",
|
|
23
23
|
"build": "tsc -b && vite build",
|
|
24
24
|
"test:cli-concurrency": "node scripts/test-cli-concurrency.mjs",
|
|
25
|
+
"test:shared-cache": "node scripts/test-shared-cache.mjs",
|
|
25
26
|
"prepack": "npm run build",
|
|
26
27
|
"preview": "vite preview --host 0.0.0.0",
|
|
27
28
|
"mcp": "node scripts/mcp-server.mjs",
|
|
@@ -33,13 +34,13 @@
|
|
|
33
34
|
"lucide-react": "^0.468.0",
|
|
34
35
|
"react": "^18.3.1",
|
|
35
36
|
"react-dom": "^18.3.1",
|
|
37
|
+
"typescript": "^5.8.3",
|
|
36
38
|
"vite": "^6.0.7",
|
|
37
39
|
"yaml": "^2.8.3"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@types/node": "^22.10.2",
|
|
41
43
|
"@types/react": "^18.3.18",
|
|
42
|
-
"@types/react-dom": "^18.3.5"
|
|
43
|
-
"typescript": "^7.0.2"
|
|
44
|
+
"@types/react-dom": "^18.3.5"
|
|
44
45
|
}
|
|
45
46
|
}
|
package/scripts/apiskill-cli.mjs
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import { crawlOpenApi, importFromCurl, importFromLocalFile, importFromUrl } from './lib/openapi-importer.mjs';
|
|
20
20
|
import { checkCache, formatCheckText, queryEndpoints } from './lib/apiskill-core.mjs';
|
|
21
21
|
import { startMockServer } from './lib/mock-server.mjs';
|
|
22
|
-
import { getDefaultCacheDir } from './lib/cache-paths.mjs';
|
|
22
|
+
import { getCacheSettings, getDefaultCacheDir, resetDefaultCacheDir, setDefaultCacheDir } from './lib/cache-paths.mjs';
|
|
23
23
|
|
|
24
24
|
const program = new Command();
|
|
25
25
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
@@ -41,6 +41,8 @@ Examples:
|
|
|
41
41
|
apiskill import-file ./openapi.json
|
|
42
42
|
apiskill import-curl --file ./request.curl
|
|
43
43
|
apiskill run web
|
|
44
|
+
apiskill cache show
|
|
45
|
+
apiskill cache set ~/.apiskill/cache
|
|
44
46
|
apiskill mock
|
|
45
47
|
apiskill document create --title "My API" --doc-version 1.0.0
|
|
46
48
|
apiskill check
|
|
@@ -59,6 +61,36 @@ CLI config format:
|
|
|
59
61
|
`,
|
|
60
62
|
);
|
|
61
63
|
|
|
64
|
+
const cache = program.command('cache').description('View or change the shared default document cache directory.');
|
|
65
|
+
|
|
66
|
+
cache
|
|
67
|
+
.command('show')
|
|
68
|
+
.description('Show the active and saved default cache directories.')
|
|
69
|
+
.option('-j, --json', 'Print JSON')
|
|
70
|
+
.action((options) => {
|
|
71
|
+
const settings = getCacheSettings();
|
|
72
|
+
print(options.json ? settings : formatCacheSettingsText(settings));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
cache
|
|
76
|
+
.command('set')
|
|
77
|
+
.description('Save the default cache directory used by future CLI, MCP, and Web processes. Existing files are not moved.')
|
|
78
|
+
.argument('<directory>', 'Absolute path or a path beginning with ~/')
|
|
79
|
+
.option('-j, --json', 'Print JSON')
|
|
80
|
+
.action(async (directory, options) => {
|
|
81
|
+
const settings = await setDefaultCacheDir(directory);
|
|
82
|
+
print(options.json ? settings : `${formatCacheSettingsText(settings)}\nExisting cached files were not moved.`);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
cache
|
|
86
|
+
.command('reset')
|
|
87
|
+
.description('Restore the built-in ~/.apiskill/cache default. Existing files are not moved.')
|
|
88
|
+
.option('-j, --json', 'Print JSON')
|
|
89
|
+
.action(async (options) => {
|
|
90
|
+
const settings = await resetDefaultCacheDir();
|
|
91
|
+
print(options.json ? settings : `${formatCacheSettingsText(settings)}\nExisting cached files were not moved.`);
|
|
92
|
+
});
|
|
93
|
+
|
|
62
94
|
const run = program.command('run').description('Run API Skill local services.');
|
|
63
95
|
|
|
64
96
|
run
|
|
@@ -67,8 +99,7 @@ run
|
|
|
67
99
|
.option('-p, --port <number>', 'Web server port', '8888')
|
|
68
100
|
.option('--host <host>', 'Host to bind', '127.0.0.1')
|
|
69
101
|
.option('--strict-port', 'Fail if the requested port is already in use')
|
|
70
|
-
.option('--cwd <dir>', 'Project directory.
|
|
71
|
-
.option('--cache-dir <dir>', 'Cache directory. Defaults to ~/.apiskill/cache')
|
|
102
|
+
.option('--cwd <dir>', 'Project working directory. Does not change the shared cache directory')
|
|
72
103
|
.action(async (options) => {
|
|
73
104
|
await runWeb(options);
|
|
74
105
|
});
|
|
@@ -297,16 +328,11 @@ function registerMockCommand(command) {
|
|
|
297
328
|
|
|
298
329
|
async function runWeb(options) {
|
|
299
330
|
const projectRoot = resolve(options.cwd || process.cwd());
|
|
300
|
-
const cacheDir =
|
|
301
|
-
? resolve(projectRoot, options.cacheDir)
|
|
302
|
-
: options.cwd
|
|
303
|
-
? resolve(projectRoot, 'cache')
|
|
304
|
-
: getDefaultCacheDir();
|
|
331
|
+
const cacheDir = getDefaultCacheDir();
|
|
305
332
|
const port = Number(options.port) || 8888;
|
|
306
333
|
const host = options.host || '127.0.0.1';
|
|
307
334
|
|
|
308
335
|
process.env.APISKILL_ROOT = projectRoot;
|
|
309
|
-
process.env.APISKILL_CACHE_DIR = cacheDir;
|
|
310
336
|
|
|
311
337
|
const { createServer } = await import('vite');
|
|
312
338
|
const server = await createServer({
|
|
@@ -379,3 +405,16 @@ function print(value) {
|
|
|
379
405
|
if (typeof value === 'string') console.log(value);
|
|
380
406
|
else console.log(JSON.stringify(value, null, 2));
|
|
381
407
|
}
|
|
408
|
+
|
|
409
|
+
function formatCacheSettingsText(settings) {
|
|
410
|
+
return [
|
|
411
|
+
`Active cache: ${settings.cacheDir}`,
|
|
412
|
+
`Saved default: ${settings.defaultCacheDir}`,
|
|
413
|
+
`Built-in default: ${settings.builtInCacheDir}`,
|
|
414
|
+
`Source: ${settings.source}`,
|
|
415
|
+
settings.environmentOverride ? `Environment override: ${settings.environmentCacheDir}` : '',
|
|
416
|
+
`Config: ${settings.configPath}`,
|
|
417
|
+
]
|
|
418
|
+
.filter(Boolean)
|
|
419
|
+
.join('\n');
|
|
420
|
+
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
saveManualOperation,
|
|
14
14
|
} from './openapi-store.mjs';
|
|
15
15
|
import { crawlOpenApi, importFromCurl, importFromLocalFile, importFromUrl } from './openapi-importer.mjs';
|
|
16
|
+
import { getDefaultCacheDir } from './cache-paths.mjs';
|
|
16
17
|
|
|
17
18
|
const MAX_DEPTH = 12;
|
|
18
19
|
|
|
@@ -37,6 +38,7 @@ export const importExamples = {
|
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
export async function checkCache() {
|
|
41
|
+
const cacheDir = getDefaultCacheDir();
|
|
40
42
|
const versions = await listCachedVersions();
|
|
41
43
|
try {
|
|
42
44
|
const { document, meta } = await readCachedDocument();
|
|
@@ -46,6 +48,7 @@ export async function checkCache() {
|
|
|
46
48
|
return {
|
|
47
49
|
ok: false,
|
|
48
50
|
message: 'No usable cached OpenAPI document found. The latest cache exists, but it has no paths.',
|
|
51
|
+
cacheDir,
|
|
49
52
|
versionsCount: versions.length,
|
|
50
53
|
latestVersion: metaSummary(meta),
|
|
51
54
|
importExamples,
|
|
@@ -54,6 +57,7 @@ export async function checkCache() {
|
|
|
54
57
|
return {
|
|
55
58
|
ok: true,
|
|
56
59
|
message: 'API Skill cache is ready.',
|
|
60
|
+
cacheDir,
|
|
57
61
|
versionsCount: versions.length,
|
|
58
62
|
latestVersion: {
|
|
59
63
|
...metaSummary(meta),
|
|
@@ -66,6 +70,7 @@ export async function checkCache() {
|
|
|
66
70
|
return {
|
|
67
71
|
ok: false,
|
|
68
72
|
message: 'No usable cached OpenAPI document found. Import or crawl a document first.',
|
|
73
|
+
cacheDir,
|
|
69
74
|
error: error instanceof Error ? error.message : 'Unknown cache read error',
|
|
70
75
|
versionsCount: versions.length,
|
|
71
76
|
latestVersion: versions.find((version) => version.latest) ?? versions[0],
|
|
@@ -76,6 +81,7 @@ export async function checkCache() {
|
|
|
76
81
|
|
|
77
82
|
export function formatCheckText(result) {
|
|
78
83
|
const lines = [];
|
|
84
|
+
if (result.cacheDir) lines.push(`Cache: ${result.cacheDir}`);
|
|
79
85
|
if (result.ok) {
|
|
80
86
|
lines.push('OK: API Skill cache is ready.');
|
|
81
87
|
if (result.latestVersion?.versionId) lines.push(`Latest version: ${result.latestVersion.versionId}`);
|
|
@@ -1,8 +1,98 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { mkdir, rename, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { dirname, resolve } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const CONFIG_FILE_NAME = 'config.json';
|
|
7
|
+
|
|
8
|
+
export function getCacheSettings(env = process.env) {
|
|
9
|
+
const appDataDir = getAppDataDir(env);
|
|
10
|
+
const configPath = resolve(appDataDir, CONFIG_FILE_NAME);
|
|
11
|
+
const builtInCacheDir = resolve(homedir(), '.apiskill', 'cache');
|
|
12
|
+
const configuredCacheDir = readConfiguredCacheDir(configPath);
|
|
13
|
+
const defaultCacheDir = configuredCacheDir || builtInCacheDir;
|
|
14
|
+
const environmentCacheDir = env.APISKILL_CACHE_DIR ? normalizeCacheDir(env.APISKILL_CACHE_DIR) : '';
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
cacheDir: environmentCacheDir || defaultCacheDir,
|
|
18
|
+
defaultCacheDir,
|
|
19
|
+
configuredCacheDir,
|
|
20
|
+
builtInCacheDir,
|
|
21
|
+
environmentCacheDir,
|
|
22
|
+
environmentOverride: Boolean(environmentCacheDir),
|
|
23
|
+
source: environmentCacheDir ? 'environment' : configuredCacheDir ? 'config' : 'default',
|
|
24
|
+
configPath,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
3
27
|
|
|
4
28
|
export function getDefaultCacheDir(env = process.env) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
29
|
+
return getCacheSettings(env).cacheDir;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function setDefaultCacheDir(value, env = process.env) {
|
|
33
|
+
const cacheDir = normalizeCacheDir(value);
|
|
34
|
+
const settings = getCacheSettings(env);
|
|
35
|
+
await mkdir(cacheDir, { recursive: true });
|
|
36
|
+
const writeProbePath = resolve(cacheDir, `.apiskill-write-test-${process.pid}-${Date.now()}`);
|
|
37
|
+
try {
|
|
38
|
+
await writeFile(writeProbePath, 'ok');
|
|
39
|
+
} finally {
|
|
40
|
+
await rm(writeProbePath, { force: true }).catch(() => {});
|
|
41
|
+
}
|
|
42
|
+
await mkdir(dirname(settings.configPath), { recursive: true });
|
|
43
|
+
|
|
44
|
+
const currentConfig = readConfig(settings.configPath);
|
|
45
|
+
const nextConfig = {
|
|
46
|
+
...currentConfig,
|
|
47
|
+
cacheDir,
|
|
48
|
+
cacheDirUpdatedAt: new Date().toISOString(),
|
|
49
|
+
};
|
|
50
|
+
const temporaryPath = `${settings.configPath}.${process.pid}.${Date.now()}.tmp`;
|
|
51
|
+
await writeFile(temporaryPath, JSON.stringify(nextConfig, null, 2));
|
|
52
|
+
await rename(temporaryPath, settings.configPath);
|
|
53
|
+
return getCacheSettings(env);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function resetDefaultCacheDir(env = process.env) {
|
|
57
|
+
const settings = getCacheSettings(env);
|
|
58
|
+
const currentConfig = readConfig(settings.configPath);
|
|
59
|
+
delete currentConfig.cacheDir;
|
|
60
|
+
currentConfig.cacheDirUpdatedAt = new Date().toISOString();
|
|
61
|
+
await mkdir(dirname(settings.configPath), { recursive: true });
|
|
62
|
+
const temporaryPath = `${settings.configPath}.${process.pid}.${Date.now()}.tmp`;
|
|
63
|
+
await writeFile(temporaryPath, JSON.stringify(currentConfig, null, 2));
|
|
64
|
+
await rename(temporaryPath, settings.configPath);
|
|
65
|
+
return getCacheSettings(env);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function normalizeCacheDir(value) {
|
|
69
|
+
const input = String(value || '').trim();
|
|
70
|
+
if (!input) throw new Error('缓存目录不能为空');
|
|
71
|
+
if (input === '~') return homedir();
|
|
72
|
+
if (input.startsWith('~/')) return resolve(homedir(), input.slice(2));
|
|
73
|
+
return resolve(input);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getAppDataDir(env) {
|
|
77
|
+
return env.APISKILL_CONFIG_DIR ? normalizeCacheDir(env.APISKILL_CONFIG_DIR) : resolve(homedir(), '.apiskill');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function readConfiguredCacheDir(configPath) {
|
|
81
|
+
const value = readConfig(configPath).cacheDir;
|
|
82
|
+
if (typeof value !== 'string' || !value.trim()) return '';
|
|
83
|
+
try {
|
|
84
|
+
return normalizeCacheDir(value);
|
|
85
|
+
} catch {
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function readConfig(configPath) {
|
|
91
|
+
if (!existsSync(configPath)) return {};
|
|
92
|
+
try {
|
|
93
|
+
const parsed = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
94
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
95
|
+
} catch {
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
8
98
|
}
|