sphica 0.6.30 → 0.6.32
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/THIRD_PARTY_NOTICES.md +33 -0
- package/dist/capture.js +267 -0
- package/dist/cli.js +1457 -97
- package/dist/deliver.js +267 -0
- package/dist/mcp-record.js +267 -0
- package/dist/mcp.js +269 -0
- package/hooks/codex.json +9 -9
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -92,7 +92,8 @@ var require_src = __commonJS(function(exports, module) {
|
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
// server/src/cli.ts
|
|
95
|
-
import
|
|
95
|
+
import fs10 from "node:fs";
|
|
96
|
+
import os4 from "node:os";
|
|
96
97
|
import path10 from "node:path";
|
|
97
98
|
|
|
98
99
|
// server/node_modules/@clack/core/dist/index.mjs
|
|
@@ -2966,7 +2967,7 @@ async function run(app, inputs, context) {
|
|
|
2966
2967
|
}
|
|
2967
2968
|
|
|
2968
2969
|
// server/src/admin.ts
|
|
2969
|
-
import
|
|
2970
|
+
import fs9 from "node:fs";
|
|
2970
2971
|
import path9 from "node:path";
|
|
2971
2972
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
2972
2973
|
|
|
@@ -3007,11 +3008,1114 @@ import os from "node:os";
|
|
|
3007
3008
|
import path2 from "node:path";
|
|
3008
3009
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3009
3010
|
|
|
3011
|
+
// server/src/codex-trust.ts
|
|
3012
|
+
import crypto from "node:crypto";
|
|
3013
|
+
|
|
3014
|
+
// server/node_modules/smol-toml/dist/error.js
|
|
3015
|
+
/*!
|
|
3016
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3017
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
3018
|
+
*
|
|
3019
|
+
* Redistribution and use in source and binary forms, with or without
|
|
3020
|
+
* modification, are permitted provided that the following conditions are met:
|
|
3021
|
+
*
|
|
3022
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
3023
|
+
* list of conditions and the following disclaimer.
|
|
3024
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
3025
|
+
* this list of conditions and the following disclaimer in the
|
|
3026
|
+
* documentation and/or other materials provided with the distribution.
|
|
3027
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
3028
|
+
* may be used to endorse or promote products derived from this software without
|
|
3029
|
+
* specific prior written permission.
|
|
3030
|
+
*
|
|
3031
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
3032
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
3033
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
3034
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
3035
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
3036
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
3037
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
3038
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
3039
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
3040
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
3041
|
+
*/
|
|
3042
|
+
function getLineColFromPtr(string, ptr) {
|
|
3043
|
+
let lines = string.slice(0, ptr).split(/\r?\n/);
|
|
3044
|
+
return [lines.length, lines.pop().length + 1];
|
|
3045
|
+
}
|
|
3046
|
+
function makeCodeBlock(string, line, column) {
|
|
3047
|
+
let lines = string.split(/\r?\n/);
|
|
3048
|
+
let codeblock = "";
|
|
3049
|
+
let numberLen = (Math.log10(line + 1) | 0) + 1;
|
|
3050
|
+
for (let i2 = line - 1;i2 <= line + 1; i2++) {
|
|
3051
|
+
let l = lines[i2 - 1];
|
|
3052
|
+
if (!l)
|
|
3053
|
+
continue;
|
|
3054
|
+
codeblock += i2.toString().padEnd(numberLen, " ");
|
|
3055
|
+
codeblock += ": ";
|
|
3056
|
+
codeblock += l;
|
|
3057
|
+
codeblock += `
|
|
3058
|
+
`;
|
|
3059
|
+
if (i2 === line) {
|
|
3060
|
+
codeblock += " ".repeat(numberLen + column + 2);
|
|
3061
|
+
codeblock += `^
|
|
3062
|
+
`;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
return codeblock;
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
class TomlError extends Error {
|
|
3069
|
+
line;
|
|
3070
|
+
column;
|
|
3071
|
+
codeblock;
|
|
3072
|
+
constructor(message, options) {
|
|
3073
|
+
const [line, column] = getLineColFromPtr(options.toml, options.ptr);
|
|
3074
|
+
const codeblock = makeCodeBlock(options.toml, line, column);
|
|
3075
|
+
super(`Invalid TOML document: ${message}
|
|
3076
|
+
|
|
3077
|
+
${codeblock}`, options);
|
|
3078
|
+
this.line = line;
|
|
3079
|
+
this.column = column;
|
|
3080
|
+
this.codeblock = codeblock;
|
|
3081
|
+
}
|
|
3082
|
+
static x(message, ctx, ptr) {
|
|
3083
|
+
throw new TomlError(message, { toml: ctx.s, ptr: ptr ?? ctx.p });
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
// server/node_modules/smol-toml/dist/primitive.js
|
|
3088
|
+
/*!
|
|
3089
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3090
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
3091
|
+
*
|
|
3092
|
+
* Redistribution and use in source and binary forms, with or without
|
|
3093
|
+
* modification, are permitted provided that the following conditions are met:
|
|
3094
|
+
*
|
|
3095
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
3096
|
+
* list of conditions and the following disclaimer.
|
|
3097
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
3098
|
+
* this list of conditions and the following disclaimer in the
|
|
3099
|
+
* documentation and/or other materials provided with the distribution.
|
|
3100
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
3101
|
+
* may be used to endorse or promote products derived from this software without
|
|
3102
|
+
* specific prior written permission.
|
|
3103
|
+
*
|
|
3104
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
3105
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
3106
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
3107
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
3108
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
3109
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
3110
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
3111
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
3112
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
3113
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
3114
|
+
*/
|
|
3115
|
+
function parseString(ctx) {
|
|
3116
|
+
let startPtr = ctx.p;
|
|
3117
|
+
let c = ctx.s.charCodeAt(ctx.p++);
|
|
3118
|
+
let first = c;
|
|
3119
|
+
let isLiteral = c === 39;
|
|
3120
|
+
let isMultiline = c === ctx.s.charCodeAt(ctx.p) && c === ctx.s.charCodeAt(ctx.p + 1);
|
|
3121
|
+
if (isMultiline) {
|
|
3122
|
+
if ((c = ctx.s.charCodeAt(ctx.p += 2)) === 10)
|
|
3123
|
+
ctx.p++;
|
|
3124
|
+
else if (c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10)
|
|
3125
|
+
ctx.p += 2;
|
|
3126
|
+
}
|
|
3127
|
+
let parsed = "";
|
|
3128
|
+
let sliceStart = ctx.p;
|
|
3129
|
+
let state = 0;
|
|
3130
|
+
for (;ctx.p < ctx.s.length; ctx.p++) {
|
|
3131
|
+
c = ctx.s.charCodeAt(ctx.p);
|
|
3132
|
+
if (isMultiline && (c === 10 || c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10)) {
|
|
3133
|
+
state = state && 3;
|
|
3134
|
+
} else if (c < 32 && c !== 9 || c === 127) {
|
|
3135
|
+
TomlError.x("control characters are not allowed in strings", ctx);
|
|
3136
|
+
} else if ((!state || state === 3) && c === first && (!isMultiline || ctx.s.charCodeAt(ctx.p + 1) === first && ctx.s.charCodeAt(ctx.p + 2) === first)) {
|
|
3137
|
+
if (isMultiline) {
|
|
3138
|
+
if (ctx.s.charCodeAt(ctx.p + 3) === first)
|
|
3139
|
+
ctx.p++;
|
|
3140
|
+
if (ctx.s.charCodeAt(ctx.p + 3) === first)
|
|
3141
|
+
ctx.p++;
|
|
3142
|
+
}
|
|
3143
|
+
if (!state) {
|
|
3144
|
+
let s = ctx.s.slice(sliceStart, ctx.p);
|
|
3145
|
+
parsed = parsed ? parsed + s : s;
|
|
3146
|
+
}
|
|
3147
|
+
ctx.p += isMultiline ? 3 : 1;
|
|
3148
|
+
return parsed;
|
|
3149
|
+
} else if (!state) {
|
|
3150
|
+
if (!isLiteral && c === 92) {
|
|
3151
|
+
parsed += ctx.s.slice(sliceStart, sliceStart = ctx.p);
|
|
3152
|
+
state = 1;
|
|
3153
|
+
}
|
|
3154
|
+
} else if (state === 1) {
|
|
3155
|
+
if (c === 120 || c === 117 || c === 85) {
|
|
3156
|
+
let errPtr = ctx.p++ - 1;
|
|
3157
|
+
let value = 0;
|
|
3158
|
+
let len = c === 120 ? 2 : c === 117 ? 4 : 8;
|
|
3159
|
+
for (let j = 0;j < len; j++, ctx.p++) {
|
|
3160
|
+
let hex = ctx.s.charCodeAt(ctx.p);
|
|
3161
|
+
let digit = hex >= 48 && hex <= 57 ? hex - 48 : hex >= 65 && hex <= 70 ? hex - 65 + 10 : hex >= 97 && hex <= 102 ? hex - 97 + 10 : -1;
|
|
3162
|
+
if (digit < 0)
|
|
3163
|
+
TomlError.x("invalid non-hex character in unicode escape", ctx);
|
|
3164
|
+
value = value << 4 | digit;
|
|
3165
|
+
}
|
|
3166
|
+
if (value < 0 || value > 1114111 || value >= 55296 && value <= 57343) {
|
|
3167
|
+
TomlError.x("invalid unicode escape", ctx, errPtr);
|
|
3168
|
+
}
|
|
3169
|
+
parsed += String.fromCodePoint(value);
|
|
3170
|
+
sliceStart = ctx.p--;
|
|
3171
|
+
state = 0;
|
|
3172
|
+
} else if (isMultiline && (c === 32 || c === 9)) {
|
|
3173
|
+
state = 2;
|
|
3174
|
+
} else {
|
|
3175
|
+
if (c === 98)
|
|
3176
|
+
parsed += "\b";
|
|
3177
|
+
else if (c === 116)
|
|
3178
|
+
parsed += "\t";
|
|
3179
|
+
else if (c === 110)
|
|
3180
|
+
parsed += `
|
|
3181
|
+
`;
|
|
3182
|
+
else if (c === 102)
|
|
3183
|
+
parsed += "\f";
|
|
3184
|
+
else if (c === 114)
|
|
3185
|
+
parsed += "\r";
|
|
3186
|
+
else if (c === 101)
|
|
3187
|
+
parsed += "\x1B";
|
|
3188
|
+
else if (c === 34)
|
|
3189
|
+
parsed += '"';
|
|
3190
|
+
else if (c === 92)
|
|
3191
|
+
parsed += "\\";
|
|
3192
|
+
else
|
|
3193
|
+
TomlError.x("unrecognised escape sequence", ctx);
|
|
3194
|
+
sliceStart = ctx.p + 1;
|
|
3195
|
+
state = 0;
|
|
3196
|
+
}
|
|
3197
|
+
} else if (c !== 32 && c !== 9) {
|
|
3198
|
+
if (state === 2)
|
|
3199
|
+
TomlError.x("invalid escape: only line-ending whitespace may be escaped", ctx, sliceStart);
|
|
3200
|
+
state = !isLiteral && c === 92 ? 1 : 0;
|
|
3201
|
+
sliceStart = ctx.p;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
TomlError.x("unfinished string", ctx, startPtr);
|
|
3205
|
+
}
|
|
3206
|
+
|
|
3207
|
+
// server/node_modules/smol-toml/dist/date.js
|
|
3208
|
+
/*!
|
|
3209
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3210
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
3211
|
+
*
|
|
3212
|
+
* Redistribution and use in source and binary forms, with or without
|
|
3213
|
+
* modification, are permitted provided that the following conditions are met:
|
|
3214
|
+
*
|
|
3215
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
3216
|
+
* list of conditions and the following disclaimer.
|
|
3217
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
3218
|
+
* this list of conditions and the following disclaimer in the
|
|
3219
|
+
* documentation and/or other materials provided with the distribution.
|
|
3220
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
3221
|
+
* may be used to endorse or promote products derived from this software without
|
|
3222
|
+
* specific prior written permission.
|
|
3223
|
+
*
|
|
3224
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
3225
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
3226
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
3227
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
3228
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
3229
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
3230
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
3231
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
3232
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
3233
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
3234
|
+
*/
|
|
3235
|
+
var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[Tt ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|z|[-+]\d{2}:\d{2})?$/i;
|
|
3236
|
+
|
|
3237
|
+
class TomlDate extends Date {
|
|
3238
|
+
#hasDate = false;
|
|
3239
|
+
#hasTime = false;
|
|
3240
|
+
#offset = null;
|
|
3241
|
+
constructor(date, fasttype, unsafeDelim) {
|
|
3242
|
+
let hasDate = true;
|
|
3243
|
+
let hasTime = true;
|
|
3244
|
+
let offset = "Z";
|
|
3245
|
+
let c;
|
|
3246
|
+
if (typeof date === "string") {
|
|
3247
|
+
if (fasttype)
|
|
3248
|
+
prep: {
|
|
3249
|
+
if (fasttype < 3) {
|
|
3250
|
+
if (+date.slice(11, 13) > 23) {
|
|
3251
|
+
date = "";
|
|
3252
|
+
break prep;
|
|
3253
|
+
}
|
|
3254
|
+
if (fasttype === 2) {
|
|
3255
|
+
offset = null;
|
|
3256
|
+
date += "Z";
|
|
3257
|
+
} else if ((c = date.charCodeAt(date.length - 1)) !== 90 && c !== 122) {
|
|
3258
|
+
offset = date.slice(date.length - 6);
|
|
3259
|
+
}
|
|
3260
|
+
if (unsafeDelim)
|
|
3261
|
+
date = date.slice(0, 10) + "T" + date.slice(11);
|
|
3262
|
+
} else if (fasttype === 4) {
|
|
3263
|
+
date = +date.slice(0, 2) > 23 ? "" : `0000-01-01T${date}Z`;
|
|
3264
|
+
}
|
|
3265
|
+
hasDate = fasttype !== 4;
|
|
3266
|
+
hasTime = fasttype !== 3;
|
|
3267
|
+
}
|
|
3268
|
+
else {
|
|
3269
|
+
let match = date.match(DATE_TIME_RE);
|
|
3270
|
+
if (match) {
|
|
3271
|
+
if (!match[1]) {
|
|
3272
|
+
hasDate = false;
|
|
3273
|
+
date = `0000-01-01T${date}`;
|
|
3274
|
+
}
|
|
3275
|
+
hasTime = !!match[2];
|
|
3276
|
+
hasTime && date[10] === " " && (date = date.replace(" ", "T"));
|
|
3277
|
+
if (match[2] && +match[2] > 23) {
|
|
3278
|
+
date = "";
|
|
3279
|
+
} else {
|
|
3280
|
+
offset = match[3] || null;
|
|
3281
|
+
if (!offset && hasTime)
|
|
3282
|
+
date += "Z";
|
|
3283
|
+
}
|
|
3284
|
+
} else {
|
|
3285
|
+
date = "";
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
super(date);
|
|
3290
|
+
if (!isNaN(this.getTime())) {
|
|
3291
|
+
this.#hasDate = hasDate;
|
|
3292
|
+
this.#hasTime = hasTime;
|
|
3293
|
+
this.#offset = offset;
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
isDateTime() {
|
|
3297
|
+
return this.#hasDate && this.#hasTime;
|
|
3298
|
+
}
|
|
3299
|
+
isLocal() {
|
|
3300
|
+
return !this.#hasDate || !this.#hasTime || !this.#offset;
|
|
3301
|
+
}
|
|
3302
|
+
isDate() {
|
|
3303
|
+
return this.#hasDate && !this.#hasTime;
|
|
3304
|
+
}
|
|
3305
|
+
isTime() {
|
|
3306
|
+
return this.#hasTime && !this.#hasDate;
|
|
3307
|
+
}
|
|
3308
|
+
isValid() {
|
|
3309
|
+
return this.#hasDate || this.#hasTime;
|
|
3310
|
+
}
|
|
3311
|
+
toISOString() {
|
|
3312
|
+
let iso = super.toISOString();
|
|
3313
|
+
if (this.isDate())
|
|
3314
|
+
return iso.slice(0, 10);
|
|
3315
|
+
if (this.isTime())
|
|
3316
|
+
return iso.slice(11, 23);
|
|
3317
|
+
if (this.#offset === null)
|
|
3318
|
+
return iso.slice(0, -1);
|
|
3319
|
+
if (this.#offset === "Z" || this.#offset === "z")
|
|
3320
|
+
return iso;
|
|
3321
|
+
let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
|
|
3322
|
+
offset = this.#offset[0] === "-" ? offset : -offset;
|
|
3323
|
+
let offsetDate = new Date(this.getTime() - offset * 60000);
|
|
3324
|
+
return offsetDate.toISOString().slice(0, -1) + this.#offset;
|
|
3325
|
+
}
|
|
3326
|
+
static wrapAsOffsetDateTime(jsDate, offset = "Z") {
|
|
3327
|
+
let date = new TomlDate(jsDate);
|
|
3328
|
+
date.#offset = offset;
|
|
3329
|
+
return date;
|
|
3330
|
+
}
|
|
3331
|
+
static wrapAsLocalDateTime(jsDate) {
|
|
3332
|
+
let date = new TomlDate(jsDate);
|
|
3333
|
+
date.#offset = null;
|
|
3334
|
+
return date;
|
|
3335
|
+
}
|
|
3336
|
+
static wrapAsLocalDate(jsDate) {
|
|
3337
|
+
let date = new TomlDate(jsDate);
|
|
3338
|
+
date.#hasTime = false;
|
|
3339
|
+
date.#offset = null;
|
|
3340
|
+
return date;
|
|
3341
|
+
}
|
|
3342
|
+
static wrapAsLocalTime(jsDate) {
|
|
3343
|
+
let date = new TomlDate(jsDate);
|
|
3344
|
+
date.#hasDate = false;
|
|
3345
|
+
date.#offset = null;
|
|
3346
|
+
return date;
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
// server/node_modules/smol-toml/dist/extract.js
|
|
3351
|
+
/*!
|
|
3352
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3353
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
3354
|
+
*
|
|
3355
|
+
* Redistribution and use in source and binary forms, with or without
|
|
3356
|
+
* modification, are permitted provided that the following conditions are met:
|
|
3357
|
+
*
|
|
3358
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
3359
|
+
* list of conditions and the following disclaimer.
|
|
3360
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
3361
|
+
* this list of conditions and the following disclaimer in the
|
|
3362
|
+
* documentation and/or other materials provided with the distribution.
|
|
3363
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
3364
|
+
* may be used to endorse or promote products derived from this software without
|
|
3365
|
+
* specific prior written permission.
|
|
3366
|
+
*
|
|
3367
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
3368
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
3369
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
3370
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
3371
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
3372
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
3373
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
3374
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
3375
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
3376
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
3377
|
+
*/
|
|
3378
|
+
function isDigit(char, base = 10) {
|
|
3379
|
+
return base === 16 ? char > 47 && char < 58 || char > 64 && char < 71 || char > 96 && char < 103 : char > 47 && char < 48 + base;
|
|
3380
|
+
}
|
|
3381
|
+
function isEndOfValue(char, delim) {
|
|
3382
|
+
return char === 32 || char === 9 || char === 10 || char === 13 || delim && (char === delim || char === 44) || char === 35;
|
|
3383
|
+
}
|
|
3384
|
+
function extractValue(ctx, end) {
|
|
3385
|
+
let errPtr = ctx.p;
|
|
3386
|
+
let c = ctx.s.charCodeAt(ctx.p);
|
|
3387
|
+
if (c === 91 || c === 123) {
|
|
3388
|
+
ctx.d-- || TomlError.x("document contains excessively nested structures. aborting.", ctx);
|
|
3389
|
+
let value = c === 91 ? parseArray(ctx) : parseInlineTable(ctx);
|
|
3390
|
+
ctx.d++;
|
|
3391
|
+
return value;
|
|
3392
|
+
}
|
|
3393
|
+
if (c === 34 || c === 39) {
|
|
3394
|
+
return parseString(ctx);
|
|
3395
|
+
}
|
|
3396
|
+
if (c === 116) {
|
|
3397
|
+
if (ctx.s.charCodeAt(++ctx.p) !== 114 || ctx.s.charCodeAt(++ctx.p) !== 117 || ctx.s.charCodeAt(++ctx.p) !== 101)
|
|
3398
|
+
TomlError.x("invalid value", ctx, errPtr);
|
|
3399
|
+
return ctx.p++, true;
|
|
3400
|
+
}
|
|
3401
|
+
if (c === 102) {
|
|
3402
|
+
if (ctx.s.charCodeAt(++ctx.p) !== 97 || ctx.s.charCodeAt(++ctx.p) !== 108 || ctx.s.charCodeAt(++ctx.p) !== 115 || ctx.s.charCodeAt(++ctx.p) !== 101)
|
|
3403
|
+
TomlError.x("invalid value", ctx, errPtr);
|
|
3404
|
+
return ctx.p++, false;
|
|
3405
|
+
}
|
|
3406
|
+
if (c === 43 || c === 45) {
|
|
3407
|
+
return parseNumber(ctx, ctx.p, ctx.s.charCodeAt(++ctx.p), 44 - c, end);
|
|
3408
|
+
}
|
|
3409
|
+
if (ctx.s.charCodeAt(ctx.p + 4) === 45 && ctx.s.charCodeAt(ctx.p + 7) === 45) {
|
|
3410
|
+
return parseDate(ctx, c, end);
|
|
3411
|
+
}
|
|
3412
|
+
if (ctx.s.charCodeAt(ctx.p + 2) === 58) {
|
|
3413
|
+
return parseTime(ctx, c, end);
|
|
3414
|
+
}
|
|
3415
|
+
return parseNumber(ctx, ctx.p, c, 0, end);
|
|
3416
|
+
}
|
|
3417
|
+
function parseNumber(ctx, startPtr, startChr, sign, endChr) {
|
|
3418
|
+
let c = startChr;
|
|
3419
|
+
let state = 0;
|
|
3420
|
+
let hasUnderscores = false;
|
|
3421
|
+
if (c === 105) {
|
|
3422
|
+
if (ctx.s.charCodeAt(++ctx.p) !== 110 || ctx.s.charCodeAt(++ctx.p) !== 102)
|
|
3423
|
+
TomlError.x("invalid value", ctx, startPtr);
|
|
3424
|
+
return ctx.p++, (sign || 1) / 0;
|
|
3425
|
+
}
|
|
3426
|
+
if (c === 110) {
|
|
3427
|
+
if (ctx.s.charCodeAt(++ctx.p) !== 97 || ctx.s.charCodeAt(++ctx.p) !== 110)
|
|
3428
|
+
TomlError.x("invalid value", ctx, startPtr);
|
|
3429
|
+
return ctx.p++, NaN;
|
|
3430
|
+
}
|
|
3431
|
+
if (c === 48) {
|
|
3432
|
+
if (++ctx.p >= ctx.s.length || isEndOfValue(c = ctx.s.charCodeAt(ctx.p), endChr))
|
|
3433
|
+
return ctx.bi === true ? 0n : 0;
|
|
3434
|
+
if (!sign) {
|
|
3435
|
+
if (c === 120)
|
|
3436
|
+
return parseIntegerBaseN(ctx, startPtr, 16, endChr);
|
|
3437
|
+
else if (c === 98)
|
|
3438
|
+
return parseIntegerBaseN(ctx, startPtr, 2, endChr);
|
|
3439
|
+
else if (c === 111)
|
|
3440
|
+
return parseIntegerBaseN(ctx, startPtr, 8, endChr);
|
|
3441
|
+
}
|
|
3442
|
+
if (c === 46)
|
|
3443
|
+
state = 2;
|
|
3444
|
+
else if (c === 101 || c === 69)
|
|
3445
|
+
state = 4;
|
|
3446
|
+
else
|
|
3447
|
+
TomlError.x("illegal leading zero", ctx, startPtr);
|
|
3448
|
+
} else if (!isDigit(c))
|
|
3449
|
+
TomlError.x("invalid value", ctx, startPtr);
|
|
3450
|
+
while (++ctx.p < ctx.s.length && (c = ctx.s.charCodeAt(ctx.p), !isEndOfValue(c, endChr))) {
|
|
3451
|
+
if (!state)
|
|
3452
|
+
state = 1;
|
|
3453
|
+
if (c === 95) {
|
|
3454
|
+
if (!(state & 1))
|
|
3455
|
+
TomlError.x("illegal underscore", ctx);
|
|
3456
|
+
state += 11;
|
|
3457
|
+
hasUnderscores = true;
|
|
3458
|
+
} else if (state === 1 && c === 46)
|
|
3459
|
+
state = 2;
|
|
3460
|
+
else if ((state === 1 || state === 3) && (c === 101 || c === 69))
|
|
3461
|
+
state = 4;
|
|
3462
|
+
else if (state === 4 && (c === 43 || c === 45)) {} else if (!isDigit(c))
|
|
3463
|
+
TomlError.x(`illegal character in numeric literal`, ctx);
|
|
3464
|
+
else if (state > 9)
|
|
3465
|
+
state -= 11;
|
|
3466
|
+
else if (!(state & 1))
|
|
3467
|
+
state++;
|
|
3468
|
+
}
|
|
3469
|
+
if (!state) {
|
|
3470
|
+
let val = (startChr - 48) * (sign || 1);
|
|
3471
|
+
return ctx.bi === true ? BigInt(val) : val;
|
|
3472
|
+
}
|
|
3473
|
+
if (!(state & 1))
|
|
3474
|
+
TomlError.x("unfinished numeric value", ctx, startPtr);
|
|
3475
|
+
let str = ctx.s.slice(startPtr, ctx.p);
|
|
3476
|
+
if (hasUnderscores)
|
|
3477
|
+
str = str.replaceAll("_", "");
|
|
3478
|
+
return state > 1 ? parseFloat(str) : parseInteger(ctx, str, 10, startPtr);
|
|
3479
|
+
}
|
|
3480
|
+
function parseIntegerBaseN(ctx, startPtr, base, endChr) {
|
|
3481
|
+
let c, underscore = 1;
|
|
3482
|
+
while (++ctx.p < ctx.s.length && (c = ctx.s.charCodeAt(ctx.p), !isEndOfValue(c, endChr))) {
|
|
3483
|
+
if (c === 95) {
|
|
3484
|
+
if (underscore & 1)
|
|
3485
|
+
TomlError.x("illegal underscore", ctx);
|
|
3486
|
+
underscore = 3;
|
|
3487
|
+
} else if (!isDigit(c, base))
|
|
3488
|
+
TomlError.x(`illegal character in numeric literal`, ctx);
|
|
3489
|
+
else if (underscore & 1)
|
|
3490
|
+
underscore--;
|
|
3491
|
+
}
|
|
3492
|
+
if (underscore & 1)
|
|
3493
|
+
TomlError.x("unfinished numeric value", ctx);
|
|
3494
|
+
let str = ctx.s.slice(startPtr + 2, ctx.p);
|
|
3495
|
+
if (underscore)
|
|
3496
|
+
str = str.replaceAll("_", "");
|
|
3497
|
+
return parseInteger(ctx, str, base, startPtr);
|
|
3498
|
+
}
|
|
3499
|
+
function parseInteger(ctx, str, base, startPtr) {
|
|
3500
|
+
if (ctx.bi !== true)
|
|
3501
|
+
int: {
|
|
3502
|
+
let val = parseInt(str, base);
|
|
3503
|
+
if (!Number.isSafeInteger(val)) {
|
|
3504
|
+
if (ctx.bi)
|
|
3505
|
+
break int;
|
|
3506
|
+
TomlError.x("integer value cannot be represented losslessly", ctx, startPtr);
|
|
3507
|
+
}
|
|
3508
|
+
return val;
|
|
3509
|
+
}
|
|
3510
|
+
return base === 10 ? BigInt(str) : BigInt((base === 2 ? "0b" : base === 8 ? "0o" : "0x") + str);
|
|
3511
|
+
}
|
|
3512
|
+
function parseDate(ctx, c, endChr) {
|
|
3513
|
+
let startPtr = ctx.p++, unsafeSeparator;
|
|
3514
|
+
if (!isDigit(c) || !isDigit(ctx.s.charCodeAt(ctx.p++)) || !isDigit(ctx.s.charCodeAt(ctx.p++)) || !isDigit(ctx.s.charCodeAt(ctx.p++))) {
|
|
3515
|
+
return parseNumber(ctx, ctx.p = startPtr, c, 0, endChr);
|
|
3516
|
+
}
|
|
3517
|
+
ctx.p += 5;
|
|
3518
|
+
if (!isDigit(ctx.s.charCodeAt(ctx.p++)))
|
|
3519
|
+
TomlError.x("invalid date-time: date part is malformed", ctx, startPtr);
|
|
3520
|
+
if (ctx.p >= ctx.s.length || ((c = ctx.s.charCodeAt(ctx.p)) !== 32 || (unsafeSeparator = true, !isDigit(ctx.s.charCodeAt(ctx.p + 1)))) && c !== 84 && c !== 116) {
|
|
3521
|
+
let t3 = ctx.s.slice(startPtr, ctx.p);
|
|
3522
|
+
return readDate(ctx, t3, 3, false, startPtr);
|
|
3523
|
+
}
|
|
3524
|
+
if (ctx.s.charCodeAt(ctx.p += 3) !== 58)
|
|
3525
|
+
TomlError.x("invalid date-time: time part is malformed", ctx, startPtr);
|
|
3526
|
+
if (ctx.s.charCodeAt(ctx.p += 3) === 58)
|
|
3527
|
+
ctx.p += 3;
|
|
3528
|
+
if (ctx.s.charCodeAt(ctx.p) === 46)
|
|
3529
|
+
while (isDigit(ctx.s.charCodeAt(++ctx.p)))
|
|
3530
|
+
;
|
|
3531
|
+
if (c = ctx.s.charCodeAt(ctx.p)) {
|
|
3532
|
+
if (c === 90 || c === 122) {
|
|
3533
|
+
let t3 = ctx.s.slice(startPtr, ++ctx.p);
|
|
3534
|
+
return readDate(ctx, t3, 1, unsafeSeparator, startPtr, "[+00:00]");
|
|
3535
|
+
}
|
|
3536
|
+
if (c === 43 || c === 45) {
|
|
3537
|
+
let t3 = ctx.s.slice(startPtr, ctx.p += 6);
|
|
3538
|
+
return readDate(ctx, t3, 1, unsafeSeparator, startPtr, !ctx.ld && "[" + ctx.s.slice(ctx.p - 6, ctx.p) + "]");
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
let t2 = ctx.s.slice(startPtr, ctx.p);
|
|
3542
|
+
return readDate(ctx, t2, 2, unsafeSeparator, startPtr);
|
|
3543
|
+
}
|
|
3544
|
+
function parseTime(ctx, c, endChr) {
|
|
3545
|
+
let start = ctx.p;
|
|
3546
|
+
if (!isDigit(c) || !isDigit(ctx.s.charCodeAt(++ctx.p))) {
|
|
3547
|
+
return parseNumber(ctx, --ctx.p, c, 0, endChr);
|
|
3548
|
+
}
|
|
3549
|
+
if (ctx.s.charCodeAt(ctx.p += 4) === 58)
|
|
3550
|
+
ctx.p += 3;
|
|
3551
|
+
if (ctx.s.charCodeAt(ctx.p) === 46)
|
|
3552
|
+
while (isDigit(ctx.s.charCodeAt(++ctx.p)))
|
|
3553
|
+
;
|
|
3554
|
+
let t2 = ctx.s.slice(start, ctx.p);
|
|
3555
|
+
return readDate(ctx, t2, 4, false, start);
|
|
3556
|
+
}
|
|
3557
|
+
function readDate(ctx, str, type, unsafeDelim, errPtr, temporalSuffix) {
|
|
3558
|
+
if (ctx.ld) {
|
|
3559
|
+
let date = new TomlDate(str, type, unsafeDelim);
|
|
3560
|
+
if (!date.isValid())
|
|
3561
|
+
TomlError.x("invalid date", ctx, errPtr);
|
|
3562
|
+
return date;
|
|
3563
|
+
}
|
|
3564
|
+
try {
|
|
3565
|
+
if (temporalSuffix)
|
|
3566
|
+
str += temporalSuffix;
|
|
3567
|
+
switch (type) {
|
|
3568
|
+
case 1:
|
|
3569
|
+
return Temporal.ZonedDateTime.from(str);
|
|
3570
|
+
case 2:
|
|
3571
|
+
return Temporal.PlainDateTime.from(str);
|
|
3572
|
+
case 3:
|
|
3573
|
+
return Temporal.PlainDate.from(str);
|
|
3574
|
+
case 4:
|
|
3575
|
+
return Temporal.PlainTime.from(str);
|
|
3576
|
+
}
|
|
3577
|
+
} catch (e) {
|
|
3578
|
+
TomlError.x(e instanceof Error ? e.message : "" + e, ctx, errPtr);
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
|
|
3582
|
+
// server/node_modules/smol-toml/dist/util.js
|
|
3583
|
+
/*!
|
|
3584
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3585
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
3586
|
+
*
|
|
3587
|
+
* Redistribution and use in source and binary forms, with or without
|
|
3588
|
+
* modification, are permitted provided that the following conditions are met:
|
|
3589
|
+
*
|
|
3590
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
3591
|
+
* list of conditions and the following disclaimer.
|
|
3592
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
3593
|
+
* this list of conditions and the following disclaimer in the
|
|
3594
|
+
* documentation and/or other materials provided with the distribution.
|
|
3595
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
3596
|
+
* may be used to endorse or promote products derived from this software without
|
|
3597
|
+
* specific prior written permission.
|
|
3598
|
+
*
|
|
3599
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
3600
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
3601
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
3602
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
3603
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
3604
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
3605
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
3606
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
3607
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
3608
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
3609
|
+
*/
|
|
3610
|
+
function skipComment(ctx) {
|
|
3611
|
+
for (;ctx.p < ctx.s.length; ctx.p++) {
|
|
3612
|
+
let c = ctx.s.charCodeAt(ctx.p);
|
|
3613
|
+
if (c === 10)
|
|
3614
|
+
break;
|
|
3615
|
+
if (c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10) {
|
|
3616
|
+
ctx.p++;
|
|
3617
|
+
break;
|
|
3618
|
+
}
|
|
3619
|
+
if (c < 32 && c !== 9 || c === 127) {
|
|
3620
|
+
TomlError.x("control characters are not allowed in comments", ctx);
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
function skipVoid(ctx, banNewLines, banComments) {
|
|
3625
|
+
let c;
|
|
3626
|
+
while (ctx.p < ctx.s.length) {
|
|
3627
|
+
while (ctx.p < ctx.s.length && ((c = ctx.s.charCodeAt(ctx.p)) === 32 || c === 9 || !banNewLines && (c === 10 || c === 13 && ctx.s.charCodeAt(ctx.p + 1) === 10)))
|
|
3628
|
+
ctx.p++;
|
|
3629
|
+
if (banComments || c !== 35)
|
|
3630
|
+
break;
|
|
3631
|
+
skipComment(ctx);
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3635
|
+
// server/node_modules/smol-toml/dist/struct.js
|
|
3636
|
+
/*!
|
|
3637
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3638
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
3639
|
+
*
|
|
3640
|
+
* Redistribution and use in source and binary forms, with or without
|
|
3641
|
+
* modification, are permitted provided that the following conditions are met:
|
|
3642
|
+
*
|
|
3643
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
3644
|
+
* list of conditions and the following disclaimer.
|
|
3645
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
3646
|
+
* this list of conditions and the following disclaimer in the
|
|
3647
|
+
* documentation and/or other materials provided with the distribution.
|
|
3648
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
3649
|
+
* may be used to endorse or promote products derived from this software without
|
|
3650
|
+
* specific prior written permission.
|
|
3651
|
+
*
|
|
3652
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
3653
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
3654
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
3655
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
3656
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
3657
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
3658
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
3659
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
3660
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
3661
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
3662
|
+
*/
|
|
3663
|
+
function parseKey(ctx, end = 61) {
|
|
3664
|
+
let startPtr;
|
|
3665
|
+
let state = 0;
|
|
3666
|
+
let parsed = [];
|
|
3667
|
+
let sliceStart;
|
|
3668
|
+
let c = ctx.s.charCodeAt(startPtr = ctx.p);
|
|
3669
|
+
do {
|
|
3670
|
+
if (c === end) {
|
|
3671
|
+
if (!state)
|
|
3672
|
+
TomlError.x("unexpected end of key", ctx);
|
|
3673
|
+
if (state === 1)
|
|
3674
|
+
parsed.push(ctx.s.slice(sliceStart, ctx.p));
|
|
3675
|
+
return ctx.p++, parsed;
|
|
3676
|
+
} else if (c === 46) {
|
|
3677
|
+
if (!state)
|
|
3678
|
+
TomlError.x("illegal empty bare key", ctx);
|
|
3679
|
+
if (state === 1)
|
|
3680
|
+
parsed.push(ctx.s.slice(sliceStart, ctx.p));
|
|
3681
|
+
state = 0;
|
|
3682
|
+
} else if (!state && (c === 34 || c === 39)) {
|
|
3683
|
+
if (c === ctx.s.charCodeAt(ctx.p + 1) && c === ctx.s.charCodeAt(ctx.p + 2))
|
|
3684
|
+
TomlError.x("illegal quoted key: multiline strings are not allowed", ctx);
|
|
3685
|
+
parsed.push(parseString(ctx));
|
|
3686
|
+
state = 2;
|
|
3687
|
+
ctx.p--;
|
|
3688
|
+
} else if (c === 32 || c === 9) {
|
|
3689
|
+
if (state === 1) {
|
|
3690
|
+
parsed.push(ctx.s.slice(sliceStart, ctx.p));
|
|
3691
|
+
state = 2;
|
|
3692
|
+
}
|
|
3693
|
+
} else if (state === 2 || c < 48 && c !== 45 || c > 57 && c < 65 || c > 90 && c < 97 && c !== 95 || c > 122) {
|
|
3694
|
+
TomlError.x("illegal character in key", ctx);
|
|
3695
|
+
} else if (!state) {
|
|
3696
|
+
state = 1;
|
|
3697
|
+
sliceStart = ctx.p;
|
|
3698
|
+
}
|
|
3699
|
+
} while (c = ctx.s.charCodeAt(++ctx.p));
|
|
3700
|
+
TomlError.x("incomplete key-value: cannot find end of key", ctx, startPtr);
|
|
3701
|
+
}
|
|
3702
|
+
function parseInlineTable(ctx) {
|
|
3703
|
+
let startPtr = ctx.p++;
|
|
3704
|
+
let res = Object.create(null);
|
|
3705
|
+
let seen = new Set;
|
|
3706
|
+
let c;
|
|
3707
|
+
while (ctx.p < ctx.s.length) {
|
|
3708
|
+
skipVoid(ctx);
|
|
3709
|
+
if ((c = ctx.s.charCodeAt(ctx.p)) === 125) {
|
|
3710
|
+
ctx.p++;
|
|
3711
|
+
return res;
|
|
3712
|
+
}
|
|
3713
|
+
let k;
|
|
3714
|
+
let t2 = res;
|
|
3715
|
+
let hasOwn = false;
|
|
3716
|
+
let errPtr = ctx.p;
|
|
3717
|
+
let key = parseKey(ctx);
|
|
3718
|
+
for (let i2 = 0;i2 < key.length; i2++) {
|
|
3719
|
+
if (i2)
|
|
3720
|
+
t2 = hasOwn ? t2[k] : t2[k] = Object.create(null);
|
|
3721
|
+
k = key[i2];
|
|
3722
|
+
if ((hasOwn = Object.hasOwn(t2, k)) && (typeof t2[k] !== "object" || seen.has(t2[k]))) {
|
|
3723
|
+
TomlError.x("trying to redefine an already defined value", ctx, errPtr);
|
|
3724
|
+
}
|
|
3725
|
+
let unsafe = k === "__proto__";
|
|
3726
|
+
if (ctx.uk && (unsafe || k === "constructor")) {
|
|
3727
|
+
t2 = ctx.uk !== 1 && TomlError.x("document contains an unsafe property", ctx, errPtr);
|
|
3728
|
+
break;
|
|
3729
|
+
}
|
|
3730
|
+
if (!hasOwn && unsafe) {
|
|
3731
|
+
Object.defineProperty(t2, k, { enumerable: true, configurable: true, writable: true });
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3734
|
+
if (hasOwn) {
|
|
3735
|
+
TomlError.x("trying to redefine an already defined value", ctx, errPtr);
|
|
3736
|
+
}
|
|
3737
|
+
skipVoid(ctx, true, true);
|
|
3738
|
+
let value = extractValue(ctx, 125);
|
|
3739
|
+
if (t2 && typeof (t2[k] = value) === "object")
|
|
3740
|
+
seen.add(value);
|
|
3741
|
+
skipVoid(ctx);
|
|
3742
|
+
if ((c = ctx.s.charCodeAt(ctx.p++)) === 125) {
|
|
3743
|
+
return res;
|
|
3744
|
+
}
|
|
3745
|
+
if (c !== 44)
|
|
3746
|
+
TomlError.x("expected comma or end of structure", ctx, ctx.p - 1);
|
|
3747
|
+
}
|
|
3748
|
+
TomlError.x("unfinished table", ctx, startPtr);
|
|
3749
|
+
}
|
|
3750
|
+
function parseArray(ctx) {
|
|
3751
|
+
let startPtr = ctx.p++;
|
|
3752
|
+
let res = [];
|
|
3753
|
+
let c;
|
|
3754
|
+
while (ctx.p < ctx.s.length) {
|
|
3755
|
+
skipVoid(ctx);
|
|
3756
|
+
if ((c = ctx.s.charCodeAt(ctx.p)) === 93) {
|
|
3757
|
+
ctx.p++;
|
|
3758
|
+
return res;
|
|
3759
|
+
}
|
|
3760
|
+
res.push(extractValue(ctx, 93));
|
|
3761
|
+
skipVoid(ctx);
|
|
3762
|
+
if ((c = ctx.s.charCodeAt(ctx.p++)) === 93) {
|
|
3763
|
+
return res;
|
|
3764
|
+
}
|
|
3765
|
+
if (c !== 44)
|
|
3766
|
+
TomlError.x("expected comma or end of structure", ctx, ctx.p - 1);
|
|
3767
|
+
}
|
|
3768
|
+
TomlError.x("unfinished array", ctx, startPtr);
|
|
3769
|
+
}
|
|
3770
|
+
|
|
3771
|
+
// server/node_modules/smol-toml/dist/parse.js
|
|
3772
|
+
/*!
|
|
3773
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3774
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
3775
|
+
*
|
|
3776
|
+
* Redistribution and use in source and binary forms, with or without
|
|
3777
|
+
* modification, are permitted provided that the following conditions are met:
|
|
3778
|
+
*
|
|
3779
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
3780
|
+
* list of conditions and the following disclaimer.
|
|
3781
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
3782
|
+
* this list of conditions and the following disclaimer in the
|
|
3783
|
+
* documentation and/or other materials provided with the distribution.
|
|
3784
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
3785
|
+
* may be used to endorse or promote products derived from this software without
|
|
3786
|
+
* specific prior written permission.
|
|
3787
|
+
*
|
|
3788
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
3789
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
3790
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
3791
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
3792
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
3793
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
3794
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
3795
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
3796
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
3797
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
3798
|
+
*/
|
|
3799
|
+
function peekTable(ctx, key, table, meta, type) {
|
|
3800
|
+
let t2 = table;
|
|
3801
|
+
let m = meta;
|
|
3802
|
+
let k;
|
|
3803
|
+
let hasOwn = false;
|
|
3804
|
+
let state;
|
|
3805
|
+
for (let i2 = 0;i2 < key.length; i2++) {
|
|
3806
|
+
if (i2) {
|
|
3807
|
+
t2 = hasOwn ? t2[k] : t2[k] = Object.create(null);
|
|
3808
|
+
m = (state = m[k]).c;
|
|
3809
|
+
if (type === 0 && (state.t === 1 || state.t === 2)) {
|
|
3810
|
+
return null;
|
|
3811
|
+
}
|
|
3812
|
+
if (state.t === 2) {
|
|
3813
|
+
let l = t2.length - 1;
|
|
3814
|
+
t2 = t2[l];
|
|
3815
|
+
m = m[l].c;
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3818
|
+
k = key[i2];
|
|
3819
|
+
if ((hasOwn = Object.hasOwn(t2, k)) && m[k]?.t === 0 && m[k]?.d) {
|
|
3820
|
+
return null;
|
|
3821
|
+
}
|
|
3822
|
+
if (!hasOwn) {
|
|
3823
|
+
let unsafe = k === "__proto__";
|
|
3824
|
+
if (ctx.uk && (unsafe || k === "constructor"))
|
|
3825
|
+
return false;
|
|
3826
|
+
if (unsafe) {
|
|
3827
|
+
Object.defineProperty(t2, k, { enumerable: true, configurable: true, writable: true });
|
|
3828
|
+
Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
|
|
3829
|
+
}
|
|
3830
|
+
m[k] = {
|
|
3831
|
+
t: i2 < key.length - 1 && type === 2 ? 3 : type,
|
|
3832
|
+
d: false,
|
|
3833
|
+
i: 0,
|
|
3834
|
+
c: Object.create(null)
|
|
3835
|
+
};
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
state = m[k];
|
|
3839
|
+
if (state.t !== type && !(type === 1 && state.t === 3)) {
|
|
3840
|
+
return null;
|
|
3841
|
+
}
|
|
3842
|
+
if (type === 2) {
|
|
3843
|
+
if (!state.d) {
|
|
3844
|
+
state.d = true;
|
|
3845
|
+
t2[k] = [];
|
|
3846
|
+
}
|
|
3847
|
+
t2[k].push(t2 = Object.create(null));
|
|
3848
|
+
state.c[state.i++] = state = { t: 1, d: false, i: 0, c: Object.create(null) };
|
|
3849
|
+
}
|
|
3850
|
+
if (state.d) {
|
|
3851
|
+
return null;
|
|
3852
|
+
}
|
|
3853
|
+
state.d = true;
|
|
3854
|
+
if (type === 1) {
|
|
3855
|
+
t2 = hasOwn ? t2[k] : t2[k] = Object.create(null);
|
|
3856
|
+
} else if (type === 0 && hasOwn) {
|
|
3857
|
+
return null;
|
|
3858
|
+
}
|
|
3859
|
+
return [k, t2, state.c];
|
|
3860
|
+
}
|
|
3861
|
+
function validateTablePeek(ctx, peek, ptr) {
|
|
3862
|
+
if (peek === null || ctx.uk === 2)
|
|
3863
|
+
TomlError.x(peek === null ? "trying to redefine an already defined table or value" : "document contains an unsafe property", ctx, ptr);
|
|
3864
|
+
}
|
|
3865
|
+
function parse(toml, options = {}) {
|
|
3866
|
+
let ctx = {
|
|
3867
|
+
s: toml,
|
|
3868
|
+
p: 0,
|
|
3869
|
+
d: options.maxDepth ?? 1000,
|
|
3870
|
+
bi: options.integersAsBigInt ?? false,
|
|
3871
|
+
ld: options.useLegacyDate ?? true,
|
|
3872
|
+
uk: options.unsafeKeyBehaviour === "throw" ? 2 : options.unsafeKeyBehaviour === "drop" ? 1 : 0
|
|
3873
|
+
};
|
|
3874
|
+
let res = Object.create(null);
|
|
3875
|
+
let meta = Object.create(null);
|
|
3876
|
+
let tmp;
|
|
3877
|
+
let skipping = false;
|
|
3878
|
+
let tbl = res;
|
|
3879
|
+
let m = meta;
|
|
3880
|
+
if (toml.charCodeAt(0) === 65279)
|
|
3881
|
+
ctx.p++;
|
|
3882
|
+
skipVoid(ctx);
|
|
3883
|
+
while (ctx.p < toml.length) {
|
|
3884
|
+
if (toml.charCodeAt(ctx.p) === 91) {
|
|
3885
|
+
let isTableArray = toml.charCodeAt(++ctx.p) === 91;
|
|
3886
|
+
tmp = ctx.p += +isTableArray;
|
|
3887
|
+
skipping = false;
|
|
3888
|
+
let k = parseKey(ctx, 93);
|
|
3889
|
+
if (isTableArray) {
|
|
3890
|
+
if (toml.charCodeAt(ctx.p) !== 93) {
|
|
3891
|
+
TomlError.x("expected end of table array declaration", ctx);
|
|
3892
|
+
}
|
|
3893
|
+
ctx.p++;
|
|
3894
|
+
}
|
|
3895
|
+
let p = peekTable(ctx, k, res, meta, isTableArray ? 2 : 1);
|
|
3896
|
+
if (!p) {
|
|
3897
|
+
validateTablePeek(ctx, p, tmp);
|
|
3898
|
+
skipping = true;
|
|
3899
|
+
} else {
|
|
3900
|
+
m = p[2];
|
|
3901
|
+
tbl = p[1];
|
|
3902
|
+
}
|
|
3903
|
+
} else {
|
|
3904
|
+
tmp = ctx.p;
|
|
3905
|
+
let k = parseKey(ctx);
|
|
3906
|
+
let p = peekTable(ctx, k, tbl, m, 0);
|
|
3907
|
+
if (!p && !skipping)
|
|
3908
|
+
validateTablePeek(ctx, p, tmp);
|
|
3909
|
+
skipVoid(ctx, true, true);
|
|
3910
|
+
let v = extractValue(ctx, undefined);
|
|
3911
|
+
if (p && !skipping)
|
|
3912
|
+
p[1][p[0]] = v;
|
|
3913
|
+
}
|
|
3914
|
+
skipVoid(ctx, true);
|
|
3915
|
+
if (ctx.p < toml.length && (tmp = toml.charCodeAt(ctx.p)) !== 10 && (tmp !== 13 || toml.charCodeAt(ctx.p + 1) !== 10)) {
|
|
3916
|
+
TomlError.x("each key-value declaration must be followed by an end-of-line", ctx);
|
|
3917
|
+
}
|
|
3918
|
+
skipVoid(ctx);
|
|
3919
|
+
}
|
|
3920
|
+
return res;
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3923
|
+
// server/node_modules/smol-toml/dist/stringify.js
|
|
3924
|
+
/*!
|
|
3925
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3926
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
3927
|
+
*
|
|
3928
|
+
* Redistribution and use in source and binary forms, with or without
|
|
3929
|
+
* modification, are permitted provided that the following conditions are met:
|
|
3930
|
+
*
|
|
3931
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
3932
|
+
* list of conditions and the following disclaimer.
|
|
3933
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
3934
|
+
* this list of conditions and the following disclaimer in the
|
|
3935
|
+
* documentation and/or other materials provided with the distribution.
|
|
3936
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
3937
|
+
* may be used to endorse or promote products derived from this software without
|
|
3938
|
+
* specific prior written permission.
|
|
3939
|
+
*
|
|
3940
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
3941
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
3942
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
3943
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
3944
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
3945
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
3946
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
3947
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
3948
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
3949
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
3950
|
+
*/
|
|
3951
|
+
var HAS_WELLFORMED = !!"".isWellFormed;
|
|
3952
|
+
|
|
3953
|
+
// server/node_modules/smol-toml/dist/index.js
|
|
3954
|
+
/*!
|
|
3955
|
+
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
3956
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
3957
|
+
*
|
|
3958
|
+
* Redistribution and use in source and binary forms, with or without
|
|
3959
|
+
* modification, are permitted provided that the following conditions are met:
|
|
3960
|
+
*
|
|
3961
|
+
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
3962
|
+
* list of conditions and the following disclaimer.
|
|
3963
|
+
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
3964
|
+
* this list of conditions and the following disclaimer in the
|
|
3965
|
+
* documentation and/or other materials provided with the distribution.
|
|
3966
|
+
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
3967
|
+
* may be used to endorse or promote products derived from this software without
|
|
3968
|
+
* specific prior written permission.
|
|
3969
|
+
*
|
|
3970
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
3971
|
+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
3972
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
3973
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
3974
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
3975
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
3976
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
3977
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
3978
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
3979
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
3980
|
+
*/
|
|
3981
|
+
|
|
3982
|
+
// server/src/codex-trust.ts
|
|
3983
|
+
var CODEX_TRUST_VERIFIED = ["0.160.0"];
|
|
3984
|
+
var LABELS = {
|
|
3985
|
+
PreToolUse: "pre_tool_use",
|
|
3986
|
+
PermissionRequest: "permission_request",
|
|
3987
|
+
PostToolUse: "post_tool_use",
|
|
3988
|
+
PreCompact: "pre_compact",
|
|
3989
|
+
PostCompact: "post_compact",
|
|
3990
|
+
SessionStart: "session_start",
|
|
3991
|
+
SessionEnd: "session_end",
|
|
3992
|
+
UserPromptSubmit: "user_prompt_submit",
|
|
3993
|
+
SubagentStart: "subagent_start",
|
|
3994
|
+
SubagentStop: "subagent_stop",
|
|
3995
|
+
Stop: "stop",
|
|
3996
|
+
Interrupt: "interrupt"
|
|
3997
|
+
};
|
|
3998
|
+
var NO_MATCHER = new Set(["UserPromptSubmit", "Stop", "Interrupt"]);
|
|
3999
|
+
var CONTEXT = new Set(["PreToolUse", "PostToolUse", "SessionStart", "UserPromptSubmit", "SubagentStart"]);
|
|
4000
|
+
var DEFAULT_CONTEXT_LIMIT = 2500;
|
|
4001
|
+
var SHORT = new Set(["SessionEnd", "Interrupt"]);
|
|
4002
|
+
var RUST_SPACE = /^[\t\n\v\f\r \u0085\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]+|[\t\n\v\f\r \u0085\u00A0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]+$/g;
|
|
4003
|
+
var isTable = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4004
|
+
var isCount = (v) => typeof v === "number" && Number.isSafeInteger(v) && v >= 0;
|
|
4005
|
+
function canonical(v) {
|
|
4006
|
+
if (Array.isArray(v))
|
|
4007
|
+
return `[${v.map(canonical).join(",")}]`;
|
|
4008
|
+
if (isTable(v))
|
|
4009
|
+
return `{${Object.keys(v).sort().map((k) => `${JSON.stringify(k)}:${canonical(v[k])}`).join(",")}}`;
|
|
4010
|
+
return JSON.stringify(v);
|
|
4011
|
+
}
|
|
4012
|
+
var sha256 = (text) => `sha256:${crypto.createHash("sha256").update(text).digest("hex")}`;
|
|
4013
|
+
function readHookStates(configToml) {
|
|
4014
|
+
const states = new Map;
|
|
4015
|
+
if (configToml === null)
|
|
4016
|
+
return states;
|
|
4017
|
+
let config;
|
|
4018
|
+
try {
|
|
4019
|
+
config = parse(configToml);
|
|
4020
|
+
} catch {
|
|
4021
|
+
return { unknown: "config.toml could not be parsed" };
|
|
4022
|
+
}
|
|
4023
|
+
const hooks = isTable(config) ? config.hooks : undefined;
|
|
4024
|
+
if (hooks === undefined)
|
|
4025
|
+
return states;
|
|
4026
|
+
const table = isTable(hooks) ? hooks.state : undefined;
|
|
4027
|
+
if (table === undefined)
|
|
4028
|
+
return states;
|
|
4029
|
+
if (!isTable(table))
|
|
4030
|
+
return { unknown: "hooks.state in config.toml is not a table" };
|
|
4031
|
+
for (const [raw, value] of Object.entries(table)) {
|
|
4032
|
+
const key = raw.replace(RUST_SPACE, "");
|
|
4033
|
+
if (!key || !isTable(value))
|
|
4034
|
+
continue;
|
|
4035
|
+
const { enabled, trusted_hash } = value;
|
|
4036
|
+
if (enabled !== undefined && typeof enabled !== "boolean" || trusted_hash !== undefined && typeof trusted_hash !== "string")
|
|
4037
|
+
continue;
|
|
4038
|
+
const s = states.get(key) ?? {};
|
|
4039
|
+
if (enabled !== undefined)
|
|
4040
|
+
s.enabled = enabled;
|
|
4041
|
+
if (trusted_hash !== undefined)
|
|
4042
|
+
s.trusted_hash = trusted_hash;
|
|
4043
|
+
states.set(key, s);
|
|
4044
|
+
}
|
|
4045
|
+
return states;
|
|
4046
|
+
}
|
|
4047
|
+
function hookTrust(hooksJson, pluginId, relativePath, platform, states) {
|
|
4048
|
+
let file;
|
|
4049
|
+
try {
|
|
4050
|
+
file = JSON.parse(hooksJson, (_key, value, context) => typeof value === "number" && /[.eE]/.test(context?.source ?? "") ? Number.NaN : value);
|
|
4051
|
+
} catch {
|
|
4052
|
+
return { unknown: "the hooks file is not JSON" };
|
|
4053
|
+
}
|
|
4054
|
+
if (isTable(file) && (Object.keys(file).some((k) => k !== "hooks" && k !== "description") || file.description != null && typeof file.description !== "string"))
|
|
4055
|
+
return { unknown: "the hooks file has a top-level field Codex would not read" };
|
|
4056
|
+
const events = isTable(file) ? file.hooks : undefined;
|
|
4057
|
+
if (!isTable(events))
|
|
4058
|
+
return { unknown: "the hooks file has no hooks table" };
|
|
4059
|
+
const hooks = [];
|
|
4060
|
+
for (const [event, groups] of Object.entries(events)) {
|
|
4061
|
+
const label = LABELS[event];
|
|
4062
|
+
if (!label)
|
|
4063
|
+
return { unknown: `the hooks file has an event Codex does not know (${event})` };
|
|
4064
|
+
if (!Array.isArray(groups))
|
|
4065
|
+
return { unknown: `${event} in the hooks file is not a list` };
|
|
4066
|
+
for (const [g, group2] of groups.entries()) {
|
|
4067
|
+
if (!isTable(group2) || !Array.isArray(group2.hooks))
|
|
4068
|
+
return { unknown: `a ${event} group has no hooks list` };
|
|
4069
|
+
if (group2.matcher !== undefined && group2.matcher !== null && typeof group2.matcher !== "string")
|
|
4070
|
+
return { unknown: `a ${event} matcher is not a string` };
|
|
4071
|
+
const matcher = NO_MATCHER.has(event) ? undefined : group2.matcher ?? undefined;
|
|
4072
|
+
for (const [h2, handler] of group2.hooks.entries()) {
|
|
4073
|
+
if (!isTable(handler) || handler.type !== "command")
|
|
4074
|
+
return { unknown: `a ${event} hook is not a command` };
|
|
4075
|
+
const { command, timeout, statusMessage, additionalContextLimit } = handler;
|
|
4076
|
+
if ("commandWindows" in handler && "command_windows" in handler)
|
|
4077
|
+
return { unknown: `a ${event} hook names its Windows command twice` };
|
|
4078
|
+
const commandWindows = handler.commandWindows ?? handler.command_windows;
|
|
4079
|
+
if (typeof command !== "string" || commandWindows !== undefined && commandWindows !== null && typeof commandWindows !== "string" || timeout !== undefined && timeout !== null && !isCount(timeout) || handler.async !== undefined && typeof handler.async !== "boolean" || statusMessage !== undefined && statusMessage !== null && typeof statusMessage !== "string" || additionalContextLimit !== undefined && additionalContextLimit !== null && !isCount(additionalContextLimit))
|
|
4080
|
+
return { unknown: `a ${event} hook has a field Codex would not read` };
|
|
4081
|
+
const chosen = platform === "win32" ? commandWindows ?? command : command;
|
|
4082
|
+
if (!chosen.trim())
|
|
4083
|
+
return { unknown: `a ${event} hook has an empty command` };
|
|
4084
|
+
const given = timeout ?? undefined;
|
|
4085
|
+
const seconds = SHORT.has(event) ? Math.min(Math.max(given ?? 1, 1), 3) : Math.max(given ?? 600, 1);
|
|
4086
|
+
const limit = CONTEXT.has(event) && additionalContextLimit != null && additionalContextLimit !== DEFAULT_CONTEXT_LIMIT ? additionalContextLimit : undefined;
|
|
4087
|
+
const normalized = {
|
|
4088
|
+
type: "command",
|
|
4089
|
+
command: chosen,
|
|
4090
|
+
timeout: seconds,
|
|
4091
|
+
async: handler.async === true
|
|
4092
|
+
};
|
|
4093
|
+
if (typeof statusMessage === "string")
|
|
4094
|
+
normalized.statusMessage = statusMessage;
|
|
4095
|
+
if (limit !== undefined)
|
|
4096
|
+
normalized.additionalContextLimit = limit;
|
|
4097
|
+
const identity = { event_name: label, hooks: [normalized] };
|
|
4098
|
+
if (typeof matcher === "string")
|
|
4099
|
+
identity.matcher = matcher;
|
|
4100
|
+
const hash = sha256(canonical(identity));
|
|
4101
|
+
const key = `${pluginId}:${relativePath}:${label}:${g}:${h2}`;
|
|
4102
|
+
const state = states.get(key);
|
|
4103
|
+
const stored = state?.trusted_hash;
|
|
4104
|
+
const trust = stored === undefined ? "untrusted" : stored === hash ? "trusted" : "modified";
|
|
4105
|
+
hooks.push({ key, hash, trust, enabled: state?.enabled !== false });
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
if (!hooks.length)
|
|
4110
|
+
return { unknown: "the hooks file defines no hooks" };
|
|
4111
|
+
return { hooks };
|
|
4112
|
+
}
|
|
4113
|
+
|
|
3010
4114
|
// server/src/panel.ts
|
|
3011
4115
|
import { stripVTControlCharacters as stripVTControlCharacters2, styleText as styleText2 } from "node:util";
|
|
3012
4116
|
|
|
3013
4117
|
// server/src/text.ts
|
|
3014
|
-
import
|
|
4118
|
+
import crypto2 from "node:crypto";
|
|
3015
4119
|
var segmenter = new Intl.Segmenter("ja", { granularity: "word" });
|
|
3016
4120
|
var HIRAGANA_ONLY = /^[\p{Script=Hiragana}ー]+$/u;
|
|
3017
4121
|
var STOP = new Set(["the", "a", "an", "of", "to", "in", "is", "and", "or", "for", "on", "it", "be"]);
|
|
@@ -3109,9 +4213,9 @@ var QUESTION = new Set([
|
|
|
3109
4213
|
...["理由", "仕組", "何", "方", "場合", "今", "件"]
|
|
3110
4214
|
]);
|
|
3111
4215
|
var FOLDED_QUESTION = new Set([...QUESTION].flatMap((w) => terms(w)));
|
|
3112
|
-
var
|
|
4216
|
+
var sha2562 = (s) => crypto2.createHash("sha256").update(s).digest();
|
|
3113
4217
|
function uuidFrom(...parts) {
|
|
3114
|
-
const b =
|
|
4218
|
+
const b = crypto2.createHash("sha256").update(parts.join("\x00")).digest().subarray(0, 16);
|
|
3115
4219
|
b[6] = (b[6] ?? 0) & 15 | 128;
|
|
3116
4220
|
b[8] = (b[8] ?? 0) & 63 | 128;
|
|
3117
4221
|
const h2 = b.toString("hex");
|
|
@@ -3469,11 +4573,18 @@ function observe(cwdRoot, platform = process.platform, env = process.env, execPa
|
|
|
3469
4573
|
} catch {}
|
|
3470
4574
|
const codexCache = path2.join(codexHome, "plugins", "cache");
|
|
3471
4575
|
const codex = [];
|
|
4576
|
+
const markets = [];
|
|
3472
4577
|
for (const market of safeDirs(codexCache)) {
|
|
3473
4578
|
for (const v of safeDirs(path2.join(codexCache, market, "sphica"))) {
|
|
3474
4579
|
codex.push(install(path2.join(codexCache, market, "sphica", v)));
|
|
4580
|
+
markets.push(market);
|
|
3475
4581
|
}
|
|
3476
4582
|
}
|
|
4583
|
+
let codexHooks;
|
|
4584
|
+
if (codex.length > 1)
|
|
4585
|
+
codexHooks = { unknown: "more than one Codex cache" };
|
|
4586
|
+
else if (codex[0] && markets[0])
|
|
4587
|
+
codexHooks = codexHookTrust(codex[0].root, `sphica@${markets[0]}`, codexHome, platform, env, execPath);
|
|
3477
4588
|
let running;
|
|
3478
4589
|
if (platform === "win32")
|
|
3479
4590
|
running = { unknown: "not checked on Windows" };
|
|
@@ -3527,7 +4638,88 @@ function observe(cwdRoot, platform = process.platform, env = process.env, execPa
|
|
|
3527
4638
|
} catch {
|
|
3528
4639
|
global = { unknown: "npm root -g failed" };
|
|
3529
4640
|
}
|
|
3530
|
-
return {
|
|
4641
|
+
return {
|
|
4642
|
+
repository,
|
|
4643
|
+
cli: install(ROOT),
|
|
4644
|
+
global,
|
|
4645
|
+
claude,
|
|
4646
|
+
claudeVersion,
|
|
4647
|
+
codex,
|
|
4648
|
+
codexCache,
|
|
4649
|
+
codexHooks,
|
|
4650
|
+
running
|
|
4651
|
+
};
|
|
4652
|
+
}
|
|
4653
|
+
function codexCommand(platform, env, execPath) {
|
|
4654
|
+
if (platform !== "win32")
|
|
4655
|
+
return ["codex", ["--version"]];
|
|
4656
|
+
const exts = (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
|
|
4657
|
+
for (const dir of (env.PATH ?? env.Path ?? "").split(";").filter(Boolean))
|
|
4658
|
+
for (const ext of exts) {
|
|
4659
|
+
const at = path2.join(dir, `codex${ext.toLowerCase()}`);
|
|
4660
|
+
if (!fs2.existsSync(at))
|
|
4661
|
+
continue;
|
|
4662
|
+
if (ext.toLowerCase() === ".exe")
|
|
4663
|
+
return [at, ["--version"]];
|
|
4664
|
+
const js = path2.join(dir, "node_modules", "@openai", "codex", "bin", "codex.js");
|
|
4665
|
+
return fs2.existsSync(js) ? [execPath, [js, "--version"]] : null;
|
|
4666
|
+
}
|
|
4667
|
+
return null;
|
|
4668
|
+
}
|
|
4669
|
+
function codexHookTrust(root, pluginId, codexHome, platform, env, execPath) {
|
|
4670
|
+
const command = codexCommand(platform, env, execPath);
|
|
4671
|
+
if (!command)
|
|
4672
|
+
return { unknown: "no codex.exe, or codex.js beside codex.cmd, on PATH" };
|
|
4673
|
+
let version2;
|
|
4674
|
+
try {
|
|
4675
|
+
const out = execFileSync(command[0], command[1], {
|
|
4676
|
+
encoding: "utf8",
|
|
4677
|
+
env,
|
|
4678
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4679
|
+
timeout: 1e4
|
|
4680
|
+
});
|
|
4681
|
+
version2 = /^codex-cli (\d+\.\d+\.\d+)$/m.exec(out)?.[1];
|
|
4682
|
+
} catch {
|
|
4683
|
+
return { unknown: "codex --version failed" };
|
|
4684
|
+
}
|
|
4685
|
+
if (!version2)
|
|
4686
|
+
return { unknown: "codex --version printed no version" };
|
|
4687
|
+
if (!CODEX_TRUST_VERIFIED.includes(version2))
|
|
4688
|
+
return {
|
|
4689
|
+
unknown: `Codex ${version2}; Sphica reads its hook trust only for ${CODEX_TRUST_VERIFIED.join(", ")}`
|
|
4690
|
+
};
|
|
4691
|
+
let file;
|
|
4692
|
+
try {
|
|
4693
|
+
const manifest = JSON.parse(fs2.readFileSync(path2.join(root, ".codex-plugin", "plugin.json"), "utf8"));
|
|
4694
|
+
if (typeof manifest.hooks !== "string")
|
|
4695
|
+
return { unknown: "the Codex plugin manifest names no hooks file" };
|
|
4696
|
+
file = path2.resolve(root, manifest.hooks);
|
|
4697
|
+
} catch {
|
|
4698
|
+
return { unknown: "the Codex plugin manifest could not be read" };
|
|
4699
|
+
}
|
|
4700
|
+
const relative = path2.relative(root, file);
|
|
4701
|
+
if (!relative || relative.startsWith("..") || path2.isAbsolute(relative))
|
|
4702
|
+
return { unknown: "the hooks file is outside the plugin" };
|
|
4703
|
+
let hooksJson;
|
|
4704
|
+
try {
|
|
4705
|
+
hooksJson = fs2.readFileSync(file, "utf8");
|
|
4706
|
+
} catch {
|
|
4707
|
+
return { unknown: "the hooks file could not be read" };
|
|
4708
|
+
}
|
|
4709
|
+
const config = path2.join(codexHome, "config.toml");
|
|
4710
|
+
let toml;
|
|
4711
|
+
try {
|
|
4712
|
+
toml = fs2.readFileSync(config, "utf8");
|
|
4713
|
+
} catch (e) {
|
|
4714
|
+
if (e.code !== "ENOENT")
|
|
4715
|
+
return { unknown: "config.toml could not be read" };
|
|
4716
|
+
toml = null;
|
|
4717
|
+
}
|
|
4718
|
+
const states = readHookStates(toml);
|
|
4719
|
+
if (!(states instanceof Map))
|
|
4720
|
+
return states;
|
|
4721
|
+
const trust = hookTrust(hooksJson, pluginId, relative.split(path2.sep).join("/"), platform, states);
|
|
4722
|
+
return "unknown" in trust ? trust : { config, hooks: trust.hooks };
|
|
3531
4723
|
}
|
|
3532
4724
|
function safeDirs(dir) {
|
|
3533
4725
|
try {
|
|
@@ -3651,6 +4843,29 @@ ${indent}${caution(note)}` : ""}`);
|
|
|
3651
4843
|
todo.add("codex");
|
|
3652
4844
|
row("Codex", x2, note);
|
|
3653
4845
|
}
|
|
4846
|
+
if (unknown(s.codexHooks))
|
|
4847
|
+
say("none", "Codex hooks", `unknown (${s.codexHooks.unknown}). Check /hooks in Codex`);
|
|
4848
|
+
else if (s.codexHooks) {
|
|
4849
|
+
const { hooks, config } = s.codexHooks;
|
|
4850
|
+
const count = (t2) => hooks.filter((h2) => h2.trust === t2).length;
|
|
4851
|
+
const trusted = count("trusted");
|
|
4852
|
+
const disabled = hooks.filter((h2) => !h2.enabled).length;
|
|
4853
|
+
const summary = `${trusted} of ${hooks.length} trusted in ${short(config)}`;
|
|
4854
|
+
if (trusted === hooks.length && !disabled)
|
|
4855
|
+
say("ok", "Codex hooks", summary);
|
|
4856
|
+
else {
|
|
4857
|
+
const parts = [
|
|
4858
|
+
[count("modified"), "modified"],
|
|
4859
|
+
[count("untrusted"), "untrusted"],
|
|
4860
|
+
[disabled, "disabled"]
|
|
4861
|
+
].filter(([n2]) => n2);
|
|
4862
|
+
const steps = [
|
|
4863
|
+
trusted < hooks.length ? "open /hooks in Codex and trust Sphica's hooks" : "",
|
|
4864
|
+
disabled ? "enable the disabled ones in /hooks if that was not intended" : ""
|
|
4865
|
+
].filter(Boolean);
|
|
4866
|
+
say("warn", "Codex hooks", `${summary}; ${parts.map(([n2, w]) => `${n2} ${w}`).join(", ")}. ${steps.join("; ")}`);
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
3654
4869
|
const x = s.codex.length === 1 ? s.codex[0] : undefined;
|
|
3655
4870
|
if (!base && s.claude && !unknown(s.claude) && x && s.claude.version === x.version) {
|
|
3656
4871
|
if (fs2.existsSync(s.claude.root) && differingFiles(s.claude.root, x.root).length) {
|
|
@@ -3799,7 +5014,7 @@ function backups(file = dbFile()) {
|
|
|
3799
5014
|
|
|
3800
5015
|
// server/src/capture.ts
|
|
3801
5016
|
import { spawn } from "node:child_process";
|
|
3802
|
-
import
|
|
5017
|
+
import fs8 from "node:fs";
|
|
3803
5018
|
import path8 from "node:path";
|
|
3804
5019
|
|
|
3805
5020
|
// server/node_modules/kysely/dist/util/object-utils.js
|
|
@@ -14511,36 +15726,36 @@ class ParseJSONResultsPlugin {
|
|
|
14511
15726
|
async transformResult(args) {
|
|
14512
15727
|
return {
|
|
14513
15728
|
...args.result,
|
|
14514
|
-
rows:
|
|
15729
|
+
rows: parseArray2(args.result.rows, "$", this.#options)
|
|
14515
15730
|
};
|
|
14516
15731
|
}
|
|
14517
15732
|
}
|
|
14518
|
-
function
|
|
15733
|
+
function parseArray2(arr, jsonPath, options) {
|
|
14519
15734
|
const target = options.objectStrategy === "create" ? new Array(arr.length) : arr;
|
|
14520
15735
|
for (let i2 = 0;i2 < arr.length; ++i2) {
|
|
14521
|
-
target[i2] =
|
|
15736
|
+
target[i2] = parse2(arr[i2], `${jsonPath}[${i2}]`, options);
|
|
14522
15737
|
}
|
|
14523
15738
|
return target;
|
|
14524
15739
|
}
|
|
14525
|
-
function
|
|
15740
|
+
function parse2(value, jsonPath, options) {
|
|
14526
15741
|
if (isString(value)) {
|
|
14527
|
-
return
|
|
15742
|
+
return parseString2(value, jsonPath, options);
|
|
14528
15743
|
}
|
|
14529
15744
|
if (Array.isArray(value)) {
|
|
14530
|
-
return
|
|
15745
|
+
return parseArray2(value, jsonPath, options);
|
|
14531
15746
|
}
|
|
14532
15747
|
if (isPlainObject(value)) {
|
|
14533
15748
|
return parseObject(value, jsonPath, options);
|
|
14534
15749
|
}
|
|
14535
15750
|
return value;
|
|
14536
15751
|
}
|
|
14537
|
-
function
|
|
15752
|
+
function parseString2(str, jsonPath, options) {
|
|
14538
15753
|
const { shouldParse } = options;
|
|
14539
15754
|
if (!shouldParse(str, jsonPath)) {
|
|
14540
15755
|
return str;
|
|
14541
15756
|
}
|
|
14542
15757
|
try {
|
|
14543
|
-
return
|
|
15758
|
+
return parse2(JSON.parse(str, (key, value, ...otherArgs) => {
|
|
14544
15759
|
if (key === "__proto__") {
|
|
14545
15760
|
return;
|
|
14546
15761
|
}
|
|
@@ -14567,7 +15782,7 @@ function parseObject(obj, jsonPath, options) {
|
|
|
14567
15782
|
if (key === "__proto__") {
|
|
14568
15783
|
continue;
|
|
14569
15784
|
}
|
|
14570
|
-
const parsed =
|
|
15785
|
+
const parsed = parse2(obj[key], `${jsonPath}."${key}"`, options);
|
|
14571
15786
|
if (key === "constructor" && isPlainObject(parsed) && Object.hasOwn(parsed, "prototype")) {
|
|
14572
15787
|
delete parsed.prototype;
|
|
14573
15788
|
}
|
|
@@ -14904,7 +16119,7 @@ var sessionId = (projectId, host, externalId) => uuidFrom(String(projectId), hos
|
|
|
14904
16119
|
|
|
14905
16120
|
// server/src/project.ts
|
|
14906
16121
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
14907
|
-
import
|
|
16122
|
+
import fs6 from "node:fs";
|
|
14908
16123
|
import os3 from "node:os";
|
|
14909
16124
|
import path6 from "node:path";
|
|
14910
16125
|
|
|
@@ -14913,6 +16128,127 @@ import path5 from "node:path";
|
|
|
14913
16128
|
var MAX_BYTES = 2 * 1024 * 1024;
|
|
14914
16129
|
var leaves = (rel) => rel === ".." || rel.startsWith(`..${path5.sep}`) || path5.isAbsolute(rel);
|
|
14915
16130
|
|
|
16131
|
+
// server/src/file-lock.ts
|
|
16132
|
+
import fs5 from "node:fs";
|
|
16133
|
+
var WAIT_MS = 5000;
|
|
16134
|
+
var POLL_MS = 40;
|
|
16135
|
+
var TRIES = 5;
|
|
16136
|
+
var sleep = (ms) => {
|
|
16137
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
16138
|
+
};
|
|
16139
|
+
function retryBusy(op) {
|
|
16140
|
+
for (let attempt = 1;; attempt++) {
|
|
16141
|
+
try {
|
|
16142
|
+
op();
|
|
16143
|
+
return;
|
|
16144
|
+
} catch (e) {
|
|
16145
|
+
const code = e.code;
|
|
16146
|
+
if (attempt >= TRIES || !(code === "EPERM" || code === "EACCES" || code === "EBUSY"))
|
|
16147
|
+
throw e;
|
|
16148
|
+
sleep(50 * attempt);
|
|
16149
|
+
}
|
|
16150
|
+
}
|
|
16151
|
+
}
|
|
16152
|
+
function heldBy(lock) {
|
|
16153
|
+
let holder = 0;
|
|
16154
|
+
try {
|
|
16155
|
+
holder = Number(fs5.readFileSync(lock, "utf8").trim()) || 0;
|
|
16156
|
+
} catch {}
|
|
16157
|
+
if (holder <= 0)
|
|
16158
|
+
return `${lock} is held by another process. Run this again when it finishes.`;
|
|
16159
|
+
try {
|
|
16160
|
+
process.kill(holder, 0);
|
|
16161
|
+
} catch (e) {
|
|
16162
|
+
if (e.code === "ESRCH")
|
|
16163
|
+
return `${lock} is held by process ${holder}, which is not running. If no sphica init is running, delete ${lock} and run this again.`;
|
|
16164
|
+
}
|
|
16165
|
+
return `${lock} is held by process ${holder}. Run this again when it finishes.`;
|
|
16166
|
+
}
|
|
16167
|
+
function withFileLock(lock, fn, waitMs = WAIT_MS) {
|
|
16168
|
+
const mine = String(process.pid);
|
|
16169
|
+
const until = performance.now() + waitMs;
|
|
16170
|
+
for (;; ) {
|
|
16171
|
+
let fd = null;
|
|
16172
|
+
let refused = null;
|
|
16173
|
+
try {
|
|
16174
|
+
fd = fs5.openSync(lock, "wx");
|
|
16175
|
+
} catch (e) {
|
|
16176
|
+
const code = e.code;
|
|
16177
|
+
if (code === "EPERM" || code === "EACCES" || code === "EBUSY")
|
|
16178
|
+
refused = e;
|
|
16179
|
+
else if (code !== "EEXIST")
|
|
16180
|
+
throw e;
|
|
16181
|
+
}
|
|
16182
|
+
if (fd !== null) {
|
|
16183
|
+
try {
|
|
16184
|
+
try {
|
|
16185
|
+
fs5.writeFileSync(fd, mine);
|
|
16186
|
+
} finally {
|
|
16187
|
+
fs5.closeSync(fd);
|
|
16188
|
+
}
|
|
16189
|
+
} catch (e) {
|
|
16190
|
+
try {
|
|
16191
|
+
retryBusy(() => fs5.rmSync(lock, { force: true }));
|
|
16192
|
+
} catch {}
|
|
16193
|
+
throw e;
|
|
16194
|
+
}
|
|
16195
|
+
break;
|
|
16196
|
+
}
|
|
16197
|
+
if (performance.now() >= until)
|
|
16198
|
+
throw refused ?? new Error(heldBy(lock));
|
|
16199
|
+
sleep(POLL_MS);
|
|
16200
|
+
}
|
|
16201
|
+
let result;
|
|
16202
|
+
try {
|
|
16203
|
+
result = fn();
|
|
16204
|
+
} catch (e) {
|
|
16205
|
+
try {
|
|
16206
|
+
release(lock, mine);
|
|
16207
|
+
} catch {}
|
|
16208
|
+
throw e;
|
|
16209
|
+
}
|
|
16210
|
+
release(lock, mine);
|
|
16211
|
+
return result;
|
|
16212
|
+
}
|
|
16213
|
+
function release(lock, mine) {
|
|
16214
|
+
const cannot = (e) => new Error(`The work finished, but Sphica could not remove ${lock} (${e.message}). Delete it before running this again.`);
|
|
16215
|
+
let held = "";
|
|
16216
|
+
try {
|
|
16217
|
+
retryBusy(() => {
|
|
16218
|
+
held = fs5.readFileSync(lock, "utf8");
|
|
16219
|
+
});
|
|
16220
|
+
} catch (e) {
|
|
16221
|
+
if (e.code === "ENOENT")
|
|
16222
|
+
return;
|
|
16223
|
+
throw cannot(e);
|
|
16224
|
+
}
|
|
16225
|
+
if (held !== mine)
|
|
16226
|
+
return;
|
|
16227
|
+
try {
|
|
16228
|
+
retryBusy(() => fs5.rmSync(lock, { force: true }));
|
|
16229
|
+
} catch (e) {
|
|
16230
|
+
throw cannot(e);
|
|
16231
|
+
}
|
|
16232
|
+
}
|
|
16233
|
+
function replaceFile(file, text) {
|
|
16234
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
16235
|
+
try {
|
|
16236
|
+
const fd = fs5.openSync(tmp, "w");
|
|
16237
|
+
try {
|
|
16238
|
+
fs5.writeFileSync(fd, text);
|
|
16239
|
+
fs5.fsyncSync(fd);
|
|
16240
|
+
} finally {
|
|
16241
|
+
fs5.closeSync(fd);
|
|
16242
|
+
}
|
|
16243
|
+
retryBusy(() => fs5.renameSync(tmp, file));
|
|
16244
|
+
} catch (e) {
|
|
16245
|
+
try {
|
|
16246
|
+
retryBusy(() => fs5.rmSync(tmp, { force: true }));
|
|
16247
|
+
} catch {}
|
|
16248
|
+
throw e;
|
|
16249
|
+
}
|
|
16250
|
+
}
|
|
16251
|
+
|
|
14916
16252
|
// server/src/project.ts
|
|
14917
16253
|
var localFile = () => path6.join(sphicaHome(), "projects.json");
|
|
14918
16254
|
var LOCAL_KEY = /^[a-z0-9][a-z0-9._-]*$/;
|
|
@@ -14956,7 +16292,7 @@ var git = (dir, ...args) => {
|
|
|
14956
16292
|
function localMap() {
|
|
14957
16293
|
let raw;
|
|
14958
16294
|
try {
|
|
14959
|
-
raw =
|
|
16295
|
+
raw = fs6.readFileSync(localFile(), "utf8");
|
|
14960
16296
|
} catch (e) {
|
|
14961
16297
|
if (e.code === "ENOENT")
|
|
14962
16298
|
return {};
|
|
@@ -15003,14 +16339,27 @@ function nameLocal(dir, name) {
|
|
|
15003
16339
|
if (place?.key.startsWith("git:"))
|
|
15004
16340
|
throw new Error(`${place.root} has a git remote, so its key is ${place.key}. Add it without --name.`);
|
|
15005
16341
|
const root = rootOf(dir);
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
16342
|
+
fs6.mkdirSync(path6.dirname(localFile()), { recursive: true, mode: 448 });
|
|
16343
|
+
withFileLock(`${localFile()}.lock`, () => {
|
|
16344
|
+
const m = localMap();
|
|
16345
|
+
m[root] = name;
|
|
16346
|
+
replaceFile(localFile(), `${JSON.stringify(m, null, 2)}
|
|
15010
16347
|
`);
|
|
16348
|
+
});
|
|
15011
16349
|
return { key: `local:${name}`, root, name };
|
|
15012
16350
|
}
|
|
15013
|
-
function
|
|
16351
|
+
function underHome(p, homes, api = path6) {
|
|
16352
|
+
for (const h2 of homes) {
|
|
16353
|
+
const rel = api.relative(h2, p);
|
|
16354
|
+
if (rel === "")
|
|
16355
|
+
return "~";
|
|
16356
|
+
if (rel !== ".." && !rel.startsWith(`..${api.sep}`) && !api.isAbsolute(rel))
|
|
16357
|
+
return `~${api.sep}${rel}`;
|
|
16358
|
+
}
|
|
16359
|
+
return p;
|
|
16360
|
+
}
|
|
16361
|
+
var projectsDir = () => path6.join(os3.homedir(), "Projects");
|
|
16362
|
+
function localRoots(roots = [projectsDir()]) {
|
|
15014
16363
|
const seen = new Map;
|
|
15015
16364
|
const add = (p) => {
|
|
15016
16365
|
if (p)
|
|
@@ -15019,7 +16368,7 @@ function localRoots(roots = [path6.join(os3.homedir(), "Projects")]) {
|
|
|
15019
16368
|
for (const r2 of roots) {
|
|
15020
16369
|
let entries = [];
|
|
15021
16370
|
try {
|
|
15022
|
-
entries =
|
|
16371
|
+
entries = fs6.readdirSync(r2, { withFileTypes: true });
|
|
15023
16372
|
} catch {
|
|
15024
16373
|
continue;
|
|
15025
16374
|
}
|
|
@@ -15028,7 +16377,7 @@ function localRoots(roots = [path6.join(os3.homedir(), "Projects")]) {
|
|
|
15028
16377
|
add(identify(path6.join(r2, e.name)));
|
|
15029
16378
|
}
|
|
15030
16379
|
for (const [root, name] of Object.entries(localMap())) {
|
|
15031
|
-
if (LOCAL_KEY.test(name) &&
|
|
16380
|
+
if (LOCAL_KEY.test(name) && fs6.existsSync(root))
|
|
15032
16381
|
add({ key: `local:${name}`, root, name });
|
|
15033
16382
|
}
|
|
15034
16383
|
const found = new Map;
|
|
@@ -15061,7 +16410,7 @@ function patchPaths(patch) {
|
|
|
15061
16410
|
|
|
15062
16411
|
// server/src/worktree.ts
|
|
15063
16412
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
15064
|
-
import
|
|
16413
|
+
import fs7 from "node:fs";
|
|
15065
16414
|
import path7 from "node:path";
|
|
15066
16415
|
var MAX_PATHS = 200;
|
|
15067
16416
|
var git2 = (root, args) => {
|
|
@@ -15096,7 +16445,7 @@ function snapshot(root) {
|
|
|
15096
16445
|
continue;
|
|
15097
16446
|
let st;
|
|
15098
16447
|
try {
|
|
15099
|
-
st =
|
|
16448
|
+
st = fs7.lstatSync(path7.join(root, p), { throwIfNoEntry: false });
|
|
15100
16449
|
} catch {
|
|
15101
16450
|
st = undefined;
|
|
15102
16451
|
}
|
|
@@ -15174,7 +16523,7 @@ ${z}`,
|
|
|
15174
16523
|
function prune(held) {
|
|
15175
16524
|
let files;
|
|
15176
16525
|
try {
|
|
15177
|
-
files =
|
|
16526
|
+
files = fs8.readdirSync(held).filter((f) => f.endsWith(".json") && !f.startsWith(".")).sort();
|
|
15178
16527
|
} catch {
|
|
15179
16528
|
return;
|
|
15180
16529
|
}
|
|
@@ -15182,21 +16531,21 @@ function prune(held) {
|
|
|
15182
16531
|
const stale = files.filter((f) => Number(f.split("-")[0]) < cutoff);
|
|
15183
16532
|
const over = files.slice(0, Math.max(0, files.length - HOLD_MAX));
|
|
15184
16533
|
for (const f of new Set([...stale, ...over]))
|
|
15185
|
-
|
|
16534
|
+
fs8.rmSync(path8.join(held, f), { force: true });
|
|
15186
16535
|
}
|
|
15187
16536
|
function spool(record) {
|
|
15188
16537
|
const dir = spoolDir();
|
|
15189
|
-
|
|
16538
|
+
fs8.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
15190
16539
|
const name = `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 10)}.json`;
|
|
15191
16540
|
const tmp = path8.join(dir, `.${name}`);
|
|
15192
|
-
|
|
15193
|
-
|
|
16541
|
+
fs8.writeFileSync(tmp, JSON.stringify(record), { mode: 384 });
|
|
16542
|
+
fs8.renameSync(tmp, path8.join(dir, name));
|
|
15194
16543
|
}
|
|
15195
16544
|
var branchOf = (root) => {
|
|
15196
16545
|
try {
|
|
15197
16546
|
const dotgit = path8.join(root, ".git");
|
|
15198
|
-
const gitdir =
|
|
15199
|
-
const h2 =
|
|
16547
|
+
const gitdir = fs8.statSync(dotgit).isFile() ? path8.resolve(root, fs8.readFileSync(dotgit, "utf8").match(/^gitdir: (.+)$/m)?.[1]?.trim() ?? "") : dotgit;
|
|
16548
|
+
const h2 = fs8.readFileSync(path8.join(gitdir, "HEAD"), "utf8").trim();
|
|
15200
16549
|
return h2.startsWith("ref: refs/heads/") ? h2.slice("ref: refs/heads/".length) : null;
|
|
15201
16550
|
} catch {
|
|
15202
16551
|
return null;
|
|
@@ -15218,7 +16567,7 @@ var INJECTED = [
|
|
|
15218
16567
|
/^(?:\d+ background agents were stopped by the user:|Background agent ".*" was stopped by the user\.)/,
|
|
15219
16568
|
/^(?:Another Claude|A peer) session sent a message(?: while you were working)?:/
|
|
15220
16569
|
];
|
|
15221
|
-
var digest = (s) =>
|
|
16570
|
+
var digest = (s) => sha2562(s).toString("hex").slice(0, 16);
|
|
15222
16571
|
function answersOf(input) {
|
|
15223
16572
|
const response = input.tool_response;
|
|
15224
16573
|
const answers = response?.answers;
|
|
@@ -15242,7 +16591,7 @@ Notes: ${notes.trim()}` : "";
|
|
|
15242
16591
|
};
|
|
15243
16592
|
}
|
|
15244
16593
|
function captureNotice(file = dbFile()) {
|
|
15245
|
-
if (!
|
|
16594
|
+
if (!fs8.existsSync(file))
|
|
15246
16595
|
return panel("sphica: no database, so conversations are not recorded", [file], "Create it with sphica init");
|
|
15247
16596
|
const s = readState();
|
|
15248
16597
|
if (s.stuck)
|
|
@@ -15259,7 +16608,7 @@ var isStart = (v) => {
|
|
|
15259
16608
|
};
|
|
15260
16609
|
function readStart(file) {
|
|
15261
16610
|
try {
|
|
15262
|
-
const s = JSON.parse(
|
|
16611
|
+
const s = JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
15263
16612
|
return isStart(s) ? s : null;
|
|
15264
16613
|
} catch {
|
|
15265
16614
|
return null;
|
|
@@ -15268,7 +16617,7 @@ function readStart(file) {
|
|
|
15268
16617
|
function readStarts(dir) {
|
|
15269
16618
|
let names;
|
|
15270
16619
|
try {
|
|
15271
|
-
names =
|
|
16620
|
+
names = fs8.readdirSync(dir);
|
|
15272
16621
|
} catch (e) {
|
|
15273
16622
|
return e.code === "ENOENT" ? [] : null;
|
|
15274
16623
|
}
|
|
@@ -15291,10 +16640,10 @@ function newestTurn(starts) {
|
|
|
15291
16640
|
return at.length === 1 ? at[0]?.turn ?? null : null;
|
|
15292
16641
|
}
|
|
15293
16642
|
function writeStart(dir, s, file = startFile(dir, s.turn)) {
|
|
15294
|
-
|
|
16643
|
+
fs8.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
15295
16644
|
const tmp = path8.join(dir, `.${path8.basename(file)}.${process.pid}.tmp`);
|
|
15296
|
-
|
|
15297
|
-
|
|
16645
|
+
fs8.writeFileSync(tmp, JSON.stringify(s), { mode: 384 });
|
|
16646
|
+
fs8.renameSync(tmp, file);
|
|
15298
16647
|
}
|
|
15299
16648
|
function openTurn(dir, turn, root) {
|
|
15300
16649
|
if (readStart(startFile(dir, turn))?.running)
|
|
@@ -15324,22 +16673,22 @@ function stopTurn(dir, turn) {
|
|
|
15324
16673
|
}
|
|
15325
16674
|
function pruneBaselines() {
|
|
15326
16675
|
const cutoff = Date.now() - HOLD_DAYS * 24 * 60 * 60 * 1000;
|
|
15327
|
-
const fresh = (file) => (
|
|
16676
|
+
const fresh = (file) => (fs8.statSync(file, { throwIfNoEntry: false })?.mtimeMs ?? Date.now()) >= cutoff;
|
|
15328
16677
|
let names;
|
|
15329
16678
|
try {
|
|
15330
|
-
names =
|
|
16679
|
+
names = fs8.readdirSync(baselineDir());
|
|
15331
16680
|
} catch {
|
|
15332
16681
|
return;
|
|
15333
16682
|
}
|
|
15334
16683
|
for (const name of names) {
|
|
15335
16684
|
const at = path8.join(baselineDir(), name);
|
|
15336
|
-
if (!
|
|
16685
|
+
if (!fs8.statSync(at, { throwIfNoEntry: false })?.isDirectory()) {
|
|
15337
16686
|
if (!fresh(at))
|
|
15338
|
-
|
|
16687
|
+
fs8.rmSync(at, { force: true });
|
|
15339
16688
|
} else {
|
|
15340
|
-
const files =
|
|
16689
|
+
const files = fs8.readdirSync(at);
|
|
15341
16690
|
if (files.length && !files.some((f) => fresh(path8.join(at, f))))
|
|
15342
|
-
|
|
16691
|
+
fs8.rmSync(at, { recursive: true, force: true });
|
|
15343
16692
|
}
|
|
15344
16693
|
}
|
|
15345
16694
|
}
|
|
@@ -15358,7 +16707,7 @@ function onHook(host, input) {
|
|
|
15358
16707
|
return { flush: false };
|
|
15359
16708
|
const file = process.env.CLAUDE_ENV_FILE;
|
|
15360
16709
|
if (file && input.session_id && /^[A-Za-z0-9_-]+$/.test(input.session_id)) {
|
|
15361
|
-
|
|
16710
|
+
fs8.appendFileSync(file, `export SPHICA_PARENT_SESSION=${input.session_id}
|
|
15362
16711
|
`);
|
|
15363
16712
|
}
|
|
15364
16713
|
attempt(pruneBaselines);
|
|
@@ -15440,13 +16789,13 @@ function onHook(host, input) {
|
|
|
15440
16789
|
}
|
|
15441
16790
|
function writeState(s) {
|
|
15442
16791
|
try {
|
|
15443
|
-
|
|
16792
|
+
fs8.writeFileSync(stateFile(), JSON.stringify(s));
|
|
15444
16793
|
} catch {}
|
|
15445
16794
|
}
|
|
15446
16795
|
function readState() {
|
|
15447
16796
|
const count = (dir) => {
|
|
15448
16797
|
try {
|
|
15449
|
-
return
|
|
16798
|
+
return fs8.readdirSync(dir).filter((f) => f.endsWith(".json") && !f.startsWith(".")).length;
|
|
15450
16799
|
} catch {
|
|
15451
16800
|
return 0;
|
|
15452
16801
|
}
|
|
@@ -15458,7 +16807,7 @@ function readState() {
|
|
|
15458
16807
|
};
|
|
15459
16808
|
let raw = {};
|
|
15460
16809
|
try {
|
|
15461
|
-
const parsed = JSON.parse(
|
|
16810
|
+
const parsed = JSON.parse(fs8.readFileSync(stateFile(), "utf8"));
|
|
15462
16811
|
if (parsed && typeof parsed === "object")
|
|
15463
16812
|
raw = parsed;
|
|
15464
16813
|
} catch {}
|
|
@@ -15473,24 +16822,24 @@ function readState() {
|
|
|
15473
16822
|
}
|
|
15474
16823
|
function lock() {
|
|
15475
16824
|
const file = path8.join(spoolDir(), ".lock");
|
|
15476
|
-
|
|
16825
|
+
fs8.mkdirSync(spoolDir(), { recursive: true, mode: 448 });
|
|
15477
16826
|
for (let attempt2 = 0;attempt2 < 2; attempt2++) {
|
|
15478
16827
|
try {
|
|
15479
|
-
|
|
16828
|
+
fs8.writeFileSync(file, String(process.pid), { flag: "wx", mode: 384 });
|
|
15480
16829
|
return () => {
|
|
15481
16830
|
try {
|
|
15482
|
-
if (
|
|
15483
|
-
|
|
16831
|
+
if (fs8.readFileSync(file, "utf8") === String(process.pid))
|
|
16832
|
+
fs8.rmSync(file, { force: true });
|
|
15484
16833
|
} catch {}
|
|
15485
16834
|
};
|
|
15486
16835
|
} catch (e) {
|
|
15487
16836
|
if (e.code !== "EEXIST")
|
|
15488
16837
|
throw e;
|
|
15489
16838
|
}
|
|
15490
|
-
const st =
|
|
16839
|
+
const st = fs8.statSync(file, { throwIfNoEntry: false });
|
|
15491
16840
|
if (!st)
|
|
15492
16841
|
continue;
|
|
15493
|
-
const holder = Number(
|
|
16842
|
+
const holder = Number(fs8.readFileSync(file, "utf8") || 0);
|
|
15494
16843
|
const fresh = Date.now() - st.mtimeMs < 5 * 60000;
|
|
15495
16844
|
const alive = (() => {
|
|
15496
16845
|
if (holder <= 0)
|
|
@@ -15503,7 +16852,7 @@ function lock() {
|
|
|
15503
16852
|
})();
|
|
15504
16853
|
if (alive && fresh)
|
|
15505
16854
|
return null;
|
|
15506
|
-
|
|
16855
|
+
fs8.rmSync(file, { force: true });
|
|
15507
16856
|
}
|
|
15508
16857
|
return null;
|
|
15509
16858
|
}
|
|
@@ -15575,7 +16924,7 @@ async function write(db, batch) {
|
|
|
15575
16924
|
truncated: x.m.truncated ? 1 : 0,
|
|
15576
16925
|
redacted: x.m.redacted ? 1 : 0,
|
|
15577
16926
|
original_bytes: x.m.originalBytes,
|
|
15578
|
-
content_hash:
|
|
16927
|
+
content_hash: sha2562(x.m.body)
|
|
15579
16928
|
}))).execute();
|
|
15580
16929
|
const after = await present();
|
|
15581
16930
|
const edits = batch.flatMap((r2) => {
|
|
@@ -15602,7 +16951,7 @@ var REJECTED = new Set([18, 19, 20, 25]);
|
|
|
15602
16951
|
var rejected = (e) => REJECTED.has(sqliteCode(e) ?? -1);
|
|
15603
16952
|
var queued = (from) => {
|
|
15604
16953
|
try {
|
|
15605
|
-
return
|
|
16954
|
+
return fs8.readdirSync(from).filter((f) => f.endsWith(".json") && !f.startsWith(".")).sort().map((name) => ({ name, from }));
|
|
15606
16955
|
} catch {
|
|
15607
16956
|
return [];
|
|
15608
16957
|
}
|
|
@@ -15613,16 +16962,16 @@ async function sendBatch(db, names) {
|
|
|
15613
16962
|
for (const { name, from } of names) {
|
|
15614
16963
|
let r2;
|
|
15615
16964
|
try {
|
|
15616
|
-
r2 = current(JSON.parse(
|
|
16965
|
+
r2 = current(JSON.parse(fs8.readFileSync(path8.join(from, name), "utf8")));
|
|
15617
16966
|
} catch {
|
|
15618
|
-
|
|
15619
|
-
|
|
16967
|
+
fs8.mkdirSync(rejectedDir(), { recursive: true, mode: 448 });
|
|
16968
|
+
fs8.renameSync(path8.join(from, name), path8.join(rejectedDir(), name));
|
|
15620
16969
|
continue;
|
|
15621
16970
|
}
|
|
15622
16971
|
if (r2)
|
|
15623
16972
|
records.push({ name, from, r: r2 });
|
|
15624
16973
|
else
|
|
15625
|
-
|
|
16974
|
+
fs8.rmSync(path8.join(from, name), { force: true });
|
|
15626
16975
|
}
|
|
15627
16976
|
let sent = 0;
|
|
15628
16977
|
let strayed = [];
|
|
@@ -15648,10 +16997,10 @@ async function sendBatch(db, names) {
|
|
|
15648
16997
|
}
|
|
15649
16998
|
}
|
|
15650
16999
|
if (bad.length) {
|
|
15651
|
-
|
|
17000
|
+
fs8.mkdirSync(rejectedDir(), { recursive: true, mode: 448 });
|
|
15652
17001
|
for (const x of bad) {
|
|
15653
17002
|
try {
|
|
15654
|
-
|
|
17003
|
+
fs8.renameSync(path8.join(x.from, x.name), path8.join(rejectedDir(), x.name));
|
|
15655
17004
|
} catch (e) {
|
|
15656
17005
|
if (e.code !== "ENOENT")
|
|
15657
17006
|
throw e;
|
|
@@ -15659,12 +17008,12 @@ async function sendBatch(db, names) {
|
|
|
15659
17008
|
}
|
|
15660
17009
|
}
|
|
15661
17010
|
if (strayed.length) {
|
|
15662
|
-
|
|
17011
|
+
fs8.mkdirSync(held, { recursive: true, mode: 448 });
|
|
15663
17012
|
for (const x of strayed) {
|
|
15664
17013
|
if (x.from === held)
|
|
15665
17014
|
continue;
|
|
15666
17015
|
try {
|
|
15667
|
-
|
|
17016
|
+
fs8.renameSync(path8.join(x.from, x.name), path8.join(held, x.name));
|
|
15668
17017
|
} catch (e) {
|
|
15669
17018
|
if (e.code !== "ENOENT")
|
|
15670
17019
|
throw e;
|
|
@@ -15674,7 +17023,7 @@ async function sendBatch(db, names) {
|
|
|
15674
17023
|
const moved = new Set([...bad, ...strayed].map((x) => x.name));
|
|
15675
17024
|
for (const x of records)
|
|
15676
17025
|
if (!moved.has(x.name))
|
|
15677
|
-
|
|
17026
|
+
fs8.rmSync(path8.join(x.from, x.name), { force: true });
|
|
15678
17027
|
return { sent, rejected: bad.length };
|
|
15679
17028
|
}
|
|
15680
17029
|
async function flush(file = dbFile(), budgetMs = FLUSH_BUDGET_MS) {
|
|
@@ -16134,7 +17483,7 @@ var STOP2 = "sphica_migration_stop";
|
|
|
16134
17483
|
var NOTE = "sphica_migration_note";
|
|
16135
17484
|
var KEEP2 = 3;
|
|
16136
17485
|
function backUp(raw, file, from) {
|
|
16137
|
-
|
|
17486
|
+
fs9.mkdirSync(backupDir(file), { recursive: true, mode: 448 });
|
|
16138
17487
|
const done = backupPath(file, from);
|
|
16139
17488
|
const partial = `${done}.partial`;
|
|
16140
17489
|
try {
|
|
@@ -16150,10 +17499,10 @@ function backUp(raw, file, from) {
|
|
|
16150
17499
|
} finally {
|
|
16151
17500
|
look.close();
|
|
16152
17501
|
}
|
|
16153
|
-
|
|
17502
|
+
fs9.renameSync(partial, done);
|
|
16154
17503
|
} catch (e) {
|
|
16155
17504
|
for (const f of [partial, `${partial}-wal`, `${partial}-shm`])
|
|
16156
|
-
|
|
17505
|
+
fs9.rmSync(f, { force: true });
|
|
16157
17506
|
throw new Error(`Could not back up ${file} before migrating it, so nothing was migrated (${e.message}). Check free space in ${backupDir(file)}.`);
|
|
16158
17507
|
}
|
|
16159
17508
|
return done;
|
|
@@ -16167,7 +17516,7 @@ function prune2(file, kept) {
|
|
|
16167
17516
|
}
|
|
16168
17517
|
for (const old of all.filter((b) => b !== kept).slice(KEEP2 - 1))
|
|
16169
17518
|
try {
|
|
16170
|
-
|
|
17519
|
+
fs9.rmSync(old, { force: true });
|
|
16171
17520
|
} catch {}
|
|
16172
17521
|
}
|
|
16173
17522
|
var versionOf = (raw) => raw.prepare("pragma user_version").get().user_version;
|
|
@@ -16259,20 +17608,20 @@ function migrate(file = dbFile(), dir = MIGRATIONS()) {
|
|
|
16259
17608
|
try {
|
|
16260
17609
|
for (let r2 = from + 1;r2 <= SCHEMA_REVISION; r2++) {
|
|
16261
17610
|
const script = MIGRATION(dir, r2);
|
|
16262
|
-
if (!
|
|
17611
|
+
if (!fs9.existsSync(script))
|
|
16263
17612
|
throw new Error(`This Sphica has no migration script for revision ${r2} (${script} is missing). Reinstall sphica (\`npm i -g sphica@${packageVersionAt(ROOT) ?? "latest"}\`), then run \`sphica init\` again.`);
|
|
16264
17613
|
raw.exec("pragma foreign_keys = off");
|
|
16265
17614
|
try {
|
|
16266
17615
|
const notes = immediate(raw, () => {
|
|
16267
17616
|
if (versionOf(raw) !== r2 - 1)
|
|
16268
17617
|
return [];
|
|
16269
|
-
if (
|
|
16270
|
-
raw.exec(
|
|
17618
|
+
if (fs9.existsSync(CHECK(dir, r2))) {
|
|
17619
|
+
raw.exec(fs9.readFileSync(CHECK(dir, r2), "utf8"));
|
|
16271
17620
|
const stops = taken(raw, STOP2);
|
|
16272
17621
|
if (stops.length)
|
|
16273
17622
|
throw new Stop(r2, stops);
|
|
16274
17623
|
}
|
|
16275
|
-
raw.exec(
|
|
17624
|
+
raw.exec(fs9.readFileSync(script, "utf8"));
|
|
16276
17625
|
if (r2 === 10)
|
|
16277
17626
|
judgeEveryRecord(raw);
|
|
16278
17627
|
const broken = raw.prepare("pragma foreign_key_check").all().length;
|
|
@@ -16297,7 +17646,7 @@ ${e.list}` : "";
|
|
|
16297
17646
|
if (now === from) {
|
|
16298
17647
|
let left = "the backup made for this run was removed";
|
|
16299
17648
|
try {
|
|
16300
|
-
|
|
17649
|
+
fs9.rmSync(backup, { force: true });
|
|
16301
17650
|
} catch {
|
|
16302
17651
|
left = `the backup made for this run is still at ${backup}; delete it yourself`;
|
|
16303
17652
|
}
|
|
@@ -16314,7 +17663,7 @@ ${e.list}` : "";
|
|
|
16314
17663
|
});
|
|
16315
17664
|
}
|
|
16316
17665
|
function dbInit(file = dbFile()) {
|
|
16317
|
-
if (
|
|
17666
|
+
if (fs9.existsSync(file)) {
|
|
16318
17667
|
const look = new DatabaseSync3(file, { readOnly: true });
|
|
16319
17668
|
let generation;
|
|
16320
17669
|
try {
|
|
@@ -16335,27 +17684,32 @@ function dbInit(file = dbFile()) {
|
|
|
16335
17684
|
}
|
|
16336
17685
|
return;
|
|
16337
17686
|
}
|
|
16338
|
-
|
|
17687
|
+
fs9.mkdirSync(path9.dirname(file), { recursive: true, mode: 448 });
|
|
16339
17688
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
16340
|
-
|
|
17689
|
+
fs9.rmSync(tmp, { force: true });
|
|
16341
17690
|
try {
|
|
16342
17691
|
withOwner(tmp, (raw) => {
|
|
16343
17692
|
raw.exec("pragma journal_mode = wal");
|
|
16344
|
-
raw.exec(
|
|
17693
|
+
raw.exec(fs9.readFileSync(SCHEMA(), "utf8"));
|
|
16345
17694
|
if (versionOf(raw) !== SCHEMA_REVISION)
|
|
16346
17695
|
throw new Error(`db/schema.sql has user_version ${versionOf(raw)}, but the code expects ${SCHEMA_REVISION}`);
|
|
16347
17696
|
optimize(raw);
|
|
16348
17697
|
}, true);
|
|
16349
|
-
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
17698
|
+
const taken2 = () => new Error(`${file} already exists (another sphica init created it first). Run this again to check it.`);
|
|
17699
|
+
withFileLock(`${file}.init.lock`, () => {
|
|
17700
|
+
if (fs9.existsSync(file))
|
|
17701
|
+
throw taken2();
|
|
17702
|
+
try {
|
|
17703
|
+
fs9.linkSync(tmp, file);
|
|
17704
|
+
} catch (e) {
|
|
17705
|
+
if (e.code === "EEXIST")
|
|
17706
|
+
throw taken2();
|
|
17707
|
+
fs9.renameSync(tmp, file);
|
|
17708
|
+
}
|
|
17709
|
+
});
|
|
16356
17710
|
} finally {
|
|
16357
17711
|
for (const f of [tmp, `${tmp}-wal`, `${tmp}-shm`])
|
|
16358
|
-
|
|
17712
|
+
fs9.rmSync(f, { force: true });
|
|
16359
17713
|
}
|
|
16360
17714
|
say(`Created: ${file} (revision ${SCHEMA_REVISION})`);
|
|
16361
17715
|
}
|
|
@@ -16391,7 +17745,7 @@ function reindex(file = dbFile()) {
|
|
|
16391
17745
|
say(`Rebuilt the index: ${plural(counts.units, "unit")}, ${plural(counts.sources, "source")}`);
|
|
16392
17746
|
}
|
|
16393
17747
|
function inspect(file = dbFile()) {
|
|
16394
|
-
const size = (f) =>
|
|
17748
|
+
const size = (f) => fs9.existsSync(f) ? fs9.statSync(f).size : 0;
|
|
16395
17749
|
return withOwner(file, (raw) => {
|
|
16396
17750
|
const check = (table) => {
|
|
16397
17751
|
try {
|
|
@@ -17115,7 +18469,7 @@ async function doctor(cwd) {
|
|
|
17115
18469
|
let usable2 = false;
|
|
17116
18470
|
if (!runtime)
|
|
17117
18471
|
say2("none", "DB", "cannot check until Node is upgraded");
|
|
17118
|
-
else if (!
|
|
18472
|
+
else if (!fs10.existsSync(file))
|
|
17119
18473
|
say2("fail", "DB", `missing (${file}). Create it with sphica init`);
|
|
17120
18474
|
else {
|
|
17121
18475
|
try {
|
|
@@ -17139,7 +18493,12 @@ async function doctor(cwd) {
|
|
|
17139
18493
|
const twice = await db.selectFrom("unit").select("project_id").where("lifecycle", "in", ["active", "candidate"]).groupBy(["project_id", "content_hash"]).having((eb) => eb.fn.countAll(), ">", 1).execute();
|
|
17140
18494
|
if (twice.length)
|
|
17141
18495
|
say2("warn", "Records", `${plural(twice.length, "set")} of live records hold the same words. Read them with Sphica's read and withdraw the extra ones with /sphica:glean`);
|
|
17142
|
-
const { found } = localRoots();
|
|
18496
|
+
const { found, ambiguous } = localRoots();
|
|
18497
|
+
const homes = [os4.homedir()];
|
|
18498
|
+
try {
|
|
18499
|
+
homes.push(fs10.realpathSync.native(os4.homedir()));
|
|
18500
|
+
} catch {}
|
|
18501
|
+
const tilde = (p) => underHome(p, homes);
|
|
17143
18502
|
const rows = await db.selectFrom("project as p").select((eb) => [
|
|
17144
18503
|
"p.key",
|
|
17145
18504
|
"p.name",
|
|
@@ -17150,7 +18509,8 @@ async function doctor(cwd) {
|
|
|
17150
18509
|
console.log(section("Projects", true));
|
|
17151
18510
|
const column = Math.max(...rows.map((x) => width(inline(x.name)))) + 2;
|
|
17152
18511
|
for (const x of rows) {
|
|
17153
|
-
const
|
|
18512
|
+
const copies = ambiguous.get(x.key);
|
|
18513
|
+
const where = copies ? ` (${copies.length} copies: ${copies.map((c) => inline(tilde(c))).sort().join(", ")})` : found.get(x.key) ? "" : ` (not found in ${inline(tilde(projectsDir()))} or the named projects)`;
|
|
17154
18514
|
console.log(indent(` ${mark("none")} ${pad(inline(x.name), column)}${plural(Number(x.records ?? 0), "record")}${x.extracted ? ` / last extraction ${new Date(x.extracted).toLocaleString("sv-SE")}` : ""}${where}`));
|
|
17155
18515
|
}
|
|
17156
18516
|
});
|
|
@@ -17195,7 +18555,7 @@ async function bindGitHub() {
|
|
|
17195
18555
|
}
|
|
17196
18556
|
async function init(flags) {
|
|
17197
18557
|
const cwd = flags.cwd ?? process.cwd();
|
|
17198
|
-
if (!
|
|
18558
|
+
if (!fs10.statSync(cwd, { throwIfNoEntry: false })?.isDirectory())
|
|
17199
18559
|
throw new Error(`${cwd} is not a directory`);
|
|
17200
18560
|
const found = identify(cwd);
|
|
17201
18561
|
if (flags.name !== undefined) {
|
|
@@ -17234,7 +18594,7 @@ async function uninstall(yes) {
|
|
|
17234
18594
|
await boxed("sphica uninstall", async () => {
|
|
17235
18595
|
if (process.env.SPHICA_HOME)
|
|
17236
18596
|
throw new Error("SPHICA_HOME is set; uninstall deletes only ~/.sphica. Unset SPHICA_HOME and run it again");
|
|
17237
|
-
if (!
|
|
18597
|
+
if (!fs10.existsSync(home))
|
|
17238
18598
|
console.log(indent(`${mark("none")} ${home} does not exist`));
|
|
17239
18599
|
else {
|
|
17240
18600
|
if (!yes) {
|
|
@@ -17246,7 +18606,7 @@ async function uninstall(yes) {
|
|
|
17246
18606
|
if (isCancel(answer) || !answer)
|
|
17247
18607
|
return "cancelled";
|
|
17248
18608
|
}
|
|
17249
|
-
|
|
18609
|
+
fs10.rmSync(home, { recursive: true, force: true });
|
|
17250
18610
|
console.log(indent(`${mark("ok")} deleted ${home}`));
|
|
17251
18611
|
}
|
|
17252
18612
|
if (where.length > 1)
|