@openshain/core 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/schema.d.ts +2 -0
- package/dist/config/schema.js +16 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/runtime.js +2 -6
- package/dist/time.d.ts +14 -0
- package/dist/time.js +50 -0
- package/package.json +1 -1
- package/src/config/schema.ts +17 -2
- package/src/index.ts +1 -0
- package/src/runtime.ts +2 -6
- package/src/time.ts +54 -0
package/dist/config/schema.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare const ConfigFileSchema: z.ZodObject<{
|
|
|
11
11
|
en: "en";
|
|
12
12
|
ja: "ja";
|
|
13
13
|
}>>;
|
|
14
|
+
timezone: z.ZodOptional<z.ZodString>;
|
|
14
15
|
}, z.core.$strict>;
|
|
15
16
|
principal: z.ZodObject<{
|
|
16
17
|
id: z.ZodString;
|
|
@@ -63,6 +64,7 @@ export interface Config {
|
|
|
63
64
|
company: {
|
|
64
65
|
name: string;
|
|
65
66
|
language: Language;
|
|
67
|
+
timezone: string;
|
|
66
68
|
};
|
|
67
69
|
principal: {
|
|
68
70
|
id: string;
|
package/dist/config/schema.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { hostTimezone, isTimezone } from "../time.js";
|
|
2
3
|
const identifier = z
|
|
3
4
|
.string()
|
|
4
5
|
.regex(/^[a-z][a-z0-9_-]*$/, "use lowercase letters, digits, _ or -, starting with a letter");
|
|
@@ -35,6 +36,16 @@ export const ConfigFileSchema = z.strictObject({
|
|
|
35
36
|
company: z.strictObject({
|
|
36
37
|
name: z.string().min(1).max(200),
|
|
37
38
|
language: z.enum(LANGUAGES).default("ja"),
|
|
39
|
+
// The company's own clock decides every business date, so a workspace answers the same
|
|
40
|
+
// whether it runs on a laptop in Tokyo or in a container set to UTC.
|
|
41
|
+
// No default in the schema: it would bake the machine that generated it into the published
|
|
42
|
+
// JSON Schema. The fallback is applied when the file is turned into a Config.
|
|
43
|
+
timezone: z
|
|
44
|
+
.string()
|
|
45
|
+
.min(1)
|
|
46
|
+
.max(100)
|
|
47
|
+
.refine(isTimezone, "not a timezone name, such as Asia/Tokyo")
|
|
48
|
+
.optional(),
|
|
38
49
|
}),
|
|
39
50
|
principal: z.strictObject({ id: identifier, name: z.string().min(1).max(200) }),
|
|
40
51
|
profession: z.strictObject({ id: identifier, instructions: z.string().min(1).max(100_000) }),
|
|
@@ -70,7 +81,11 @@ export const ConfigFileSchema = z.strictObject({
|
|
|
70
81
|
export function toConfig(file) {
|
|
71
82
|
return {
|
|
72
83
|
version: file.version,
|
|
73
|
-
company: {
|
|
84
|
+
company: {
|
|
85
|
+
name: file.company.name,
|
|
86
|
+
language: file.company.language,
|
|
87
|
+
timezone: file.company.timezone ?? hostTimezone(),
|
|
88
|
+
},
|
|
74
89
|
principal: { id: file.principal.id, name: file.principal.name },
|
|
75
90
|
profession: { id: file.profession.id, instructions: file.profession.instructions },
|
|
76
91
|
...(file.model && {
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { type EventId, newEventId, newWorkId, parseEventId, parseWorkId, type Wo
|
|
|
7
7
|
export type { ModelDescription, ModelMessage, ModelProvider, ModelRequest, ModelResponse, UserPart, } from "./model/types.ts";
|
|
8
8
|
export { type CallOptions, type CreateRuntimeOptions, createRuntime, createToolCaller, createToolRegistry, MAX_TOOL_TEXT_CHARS, type PendingApprovalResult, REVIEW_DIR_NAME, type Runtime, type RuntimeProviders, type ToolSummary, } from "./runtime.ts";
|
|
9
9
|
export { jsonSchemas, type SchemaName } from "./schemas.ts";
|
|
10
|
+
export { businessDate, companyTime, hostTimezone, isTimezone } from "./time.ts";
|
|
10
11
|
export { ASK_USER, RUNTIME_PROVIDER_ID } from "./tool/ask-user.ts";
|
|
11
12
|
export { loadToolModule } from "./tool/load-module.ts";
|
|
12
13
|
export { RESERVED_PATHS, resolveWorkspacePath } from "./tool/paths.ts";
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { ERROR_CODES, isOpenshainError, OpenshainError } from "./errors.js";
|
|
|
6
6
|
export { newEventId, newWorkId, parseEventId, parseWorkId, } from "./ids.js";
|
|
7
7
|
export { createRuntime, createToolCaller, createToolRegistry, MAX_TOOL_TEXT_CHARS, REVIEW_DIR_NAME, } from "./runtime.js";
|
|
8
8
|
export { jsonSchemas } from "./schemas.js";
|
|
9
|
+
export { businessDate, companyTime, hostTimezone, isTimezone } from "./time.js";
|
|
9
10
|
export { ASK_USER, RUNTIME_PROVIDER_ID } from "./tool/ask-user.js";
|
|
10
11
|
export { loadToolModule } from "./tool/load-module.js";
|
|
11
12
|
export { RESERVED_PATHS, resolveWorkspacePath } from "./tool/paths.js";
|
package/dist/runtime.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { evaluate, loadAuthority, OPEN_AUTHORITY, } from "./authority/policy.js";
|
|
4
4
|
import { loadConfig } from "./config/load.js";
|
|
5
5
|
import { isOpenshainError, OpenshainError } from "./errors.js";
|
|
6
|
+
import { businessDate } from "./time.js";
|
|
6
7
|
import { loadToolModule } from "./tool/load-module.js";
|
|
7
8
|
import { ToolRegistry } from "./tool/registry.js";
|
|
8
9
|
import { uuidv7 } from "./uuid.js";
|
|
@@ -112,7 +113,7 @@ async function callTool(input) {
|
|
|
112
113
|
principal: config.principal.id,
|
|
113
114
|
profession: config.profession.id,
|
|
114
115
|
workType: (await work.current()).type,
|
|
115
|
-
businessDate: businessDate(),
|
|
116
|
+
businessDate: businessDate(config.company.timezone),
|
|
116
117
|
});
|
|
117
118
|
if (judged.kind === "deny")
|
|
118
119
|
return reject("denied", judged.reason);
|
|
@@ -264,11 +265,6 @@ function pathOf(input) {
|
|
|
264
265
|
return segments.join("/");
|
|
265
266
|
}
|
|
266
267
|
/** Today's date on this machine's clock, YYYY-MM-DD. */
|
|
267
|
-
function businessDate() {
|
|
268
|
-
const now = new Date();
|
|
269
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
270
|
-
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
|
|
271
|
-
}
|
|
272
268
|
function isRejectionCode(code) {
|
|
273
269
|
return TOOL_REJECTION_CODES.includes(code);
|
|
274
270
|
}
|
package/dist/time.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The company's clock. Every date the runtime judges against — a decision's effective days, a
|
|
3
|
+
* delegation's validity — is a date in the company's timezone, not in the host's. A workspace
|
|
4
|
+
* carried between a laptop in Tokyo and a container in UTC has to answer the same question the
|
|
5
|
+
* same way, so the timezone is part of the configuration rather than the environment.
|
|
6
|
+
*/
|
|
7
|
+
/** True when the name is a timezone this runtime knows (`Asia/Tokyo`, `UTC`, ...). */
|
|
8
|
+
export declare function isTimezone(name: string): boolean;
|
|
9
|
+
/** The timezone of the machine, used when the configuration names none. */
|
|
10
|
+
export declare function hostTimezone(): string;
|
|
11
|
+
/** The business date (`YYYY-MM-DD`) in the company's timezone. */
|
|
12
|
+
export declare function businessDate(timezone: string, at?: Date): string;
|
|
13
|
+
/** The moment as the company reads it: `2026-09-09T14:03:11+09:00`. */
|
|
14
|
+
export declare function companyTime(timezone: string, at?: Date): string;
|
package/dist/time.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The company's clock. Every date the runtime judges against — a decision's effective days, a
|
|
3
|
+
* delegation's validity — is a date in the company's timezone, not in the host's. A workspace
|
|
4
|
+
* carried between a laptop in Tokyo and a container in UTC has to answer the same question the
|
|
5
|
+
* same way, so the timezone is part of the configuration rather than the environment.
|
|
6
|
+
*/
|
|
7
|
+
/** True when the name is a timezone this runtime knows (`Asia/Tokyo`, `UTC`, ...). */
|
|
8
|
+
export function isTimezone(name) {
|
|
9
|
+
try {
|
|
10
|
+
new Intl.DateTimeFormat("en-US", { timeZone: name });
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** The timezone of the machine, used when the configuration names none. */
|
|
18
|
+
export function hostTimezone() {
|
|
19
|
+
const name = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
20
|
+
return name && isTimezone(name) ? name : "UTC";
|
|
21
|
+
}
|
|
22
|
+
/** The business date (`YYYY-MM-DD`) in the company's timezone. */
|
|
23
|
+
export function businessDate(timezone, at = new Date()) {
|
|
24
|
+
// en-CA writes a date as YYYY-MM-DD.
|
|
25
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
26
|
+
timeZone: timezone,
|
|
27
|
+
year: "numeric",
|
|
28
|
+
month: "2-digit",
|
|
29
|
+
day: "2-digit",
|
|
30
|
+
}).format(at);
|
|
31
|
+
}
|
|
32
|
+
/** The moment as the company reads it: `2026-09-09T14:03:11+09:00`. */
|
|
33
|
+
export function companyTime(timezone, at = new Date()) {
|
|
34
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
35
|
+
timeZone: timezone,
|
|
36
|
+
hour12: false,
|
|
37
|
+
year: "numeric",
|
|
38
|
+
month: "2-digit",
|
|
39
|
+
day: "2-digit",
|
|
40
|
+
hour: "2-digit",
|
|
41
|
+
minute: "2-digit",
|
|
42
|
+
second: "2-digit",
|
|
43
|
+
timeZoneName: "longOffset",
|
|
44
|
+
}).formatToParts(at);
|
|
45
|
+
const of = (type) => parts.find((part) => part.type === type)?.value ?? "";
|
|
46
|
+
// "GMT+09:00" for a zone with an offset, "GMT" for UTC itself.
|
|
47
|
+
const zone = of("timeZoneName").replace("GMT", "");
|
|
48
|
+
const hour = of("hour") === "24" ? "00" : of("hour");
|
|
49
|
+
return `${of("year")}-${of("month")}-${of("day")}T${hour}:${of("minute")}:${of("second")}${zone === "" ? "+00:00" : zone}`;
|
|
50
|
+
}
|
package/package.json
CHANGED
package/src/config/schema.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { hostTimezone, isTimezone } from "../time.ts";
|
|
2
3
|
|
|
3
4
|
const identifier = z
|
|
4
5
|
.string()
|
|
@@ -43,6 +44,16 @@ export const ConfigFileSchema = z.strictObject({
|
|
|
43
44
|
company: z.strictObject({
|
|
44
45
|
name: z.string().min(1).max(200),
|
|
45
46
|
language: z.enum(LANGUAGES).default("ja"),
|
|
47
|
+
// The company's own clock decides every business date, so a workspace answers the same
|
|
48
|
+
// whether it runs on a laptop in Tokyo or in a container set to UTC.
|
|
49
|
+
// No default in the schema: it would bake the machine that generated it into the published
|
|
50
|
+
// JSON Schema. The fallback is applied when the file is turned into a Config.
|
|
51
|
+
timezone: z
|
|
52
|
+
.string()
|
|
53
|
+
.min(1)
|
|
54
|
+
.max(100)
|
|
55
|
+
.refine(isTimezone, "not a timezone name, such as Asia/Tokyo")
|
|
56
|
+
.optional(),
|
|
46
57
|
}),
|
|
47
58
|
principal: z.strictObject({ id: identifier, name: z.string().min(1).max(200) }),
|
|
48
59
|
profession: z.strictObject({ id: identifier, instructions: z.string().min(1).max(100_000) }),
|
|
@@ -96,7 +107,7 @@ export interface ModelConfig {
|
|
|
96
107
|
|
|
97
108
|
export interface Config {
|
|
98
109
|
version: 1;
|
|
99
|
-
company: { name: string; language: Language };
|
|
110
|
+
company: { name: string; language: Language; timezone: string };
|
|
100
111
|
principal: { id: string; name: string };
|
|
101
112
|
profession: { id: string; instructions: string };
|
|
102
113
|
/** The model the interactive CLI runs on. Absent when the workspace is used from other agents only. */
|
|
@@ -109,7 +120,11 @@ export interface Config {
|
|
|
109
120
|
export function toConfig(file: ConfigFile): Config {
|
|
110
121
|
return {
|
|
111
122
|
version: file.version,
|
|
112
|
-
company: {
|
|
123
|
+
company: {
|
|
124
|
+
name: file.company.name,
|
|
125
|
+
language: file.company.language,
|
|
126
|
+
timezone: file.company.timezone ?? hostTimezone(),
|
|
127
|
+
},
|
|
113
128
|
principal: { id: file.principal.id, name: file.principal.name },
|
|
114
129
|
profession: { id: file.profession.id, instructions: file.profession.instructions },
|
|
115
130
|
...(file.model && {
|
package/src/index.ts
CHANGED
|
@@ -62,6 +62,7 @@ export {
|
|
|
62
62
|
type ToolSummary,
|
|
63
63
|
} from "./runtime.ts";
|
|
64
64
|
export { jsonSchemas, type SchemaName } from "./schemas.ts";
|
|
65
|
+
export { businessDate, companyTime, hostTimezone, isTimezone } from "./time.ts";
|
|
65
66
|
export { ASK_USER, RUNTIME_PROVIDER_ID } from "./tool/ask-user.ts";
|
|
66
67
|
export { loadToolModule } from "./tool/load-module.ts";
|
|
67
68
|
export { RESERVED_PATHS, resolveWorkspacePath } from "./tool/paths.ts";
|
package/src/runtime.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { loadConfig } from "./config/load.ts";
|
|
|
11
11
|
import type { Config, ModelConfig } from "./config/schema.ts";
|
|
12
12
|
import { isOpenshainError, OpenshainError } from "./errors.ts";
|
|
13
13
|
import type { ModelProvider } from "./model/types.ts";
|
|
14
|
+
import { businessDate } from "./time.ts";
|
|
14
15
|
import { loadToolModule } from "./tool/load-module.ts";
|
|
15
16
|
import type { HiddenTool } from "./tool/registry.ts";
|
|
16
17
|
import { type RegisteredTool, ToolRegistry } from "./tool/registry.ts";
|
|
@@ -215,7 +216,7 @@ async function callTool(input: {
|
|
|
215
216
|
principal: config.principal.id,
|
|
216
217
|
profession: config.profession.id,
|
|
217
218
|
workType: (await work.current()).type,
|
|
218
|
-
businessDate: businessDate(),
|
|
219
|
+
businessDate: businessDate(config.company.timezone),
|
|
219
220
|
});
|
|
220
221
|
if (judged.kind === "deny") return reject("denied", judged.reason);
|
|
221
222
|
if (judged.kind === "approval_required" || judged.kind === "review_required") {
|
|
@@ -370,11 +371,6 @@ function pathOf(input: unknown): string | undefined {
|
|
|
370
371
|
}
|
|
371
372
|
|
|
372
373
|
/** Today's date on this machine's clock, YYYY-MM-DD. */
|
|
373
|
-
function businessDate(): string {
|
|
374
|
-
const now = new Date();
|
|
375
|
-
const pad = (n: number) => String(n).padStart(2, "0");
|
|
376
|
-
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
|
|
377
|
-
}
|
|
378
374
|
|
|
379
375
|
function isRejectionCode(code: string): code is ToolRejectionCode {
|
|
380
376
|
return (TOOL_REJECTION_CODES as readonly string[]).includes(code);
|
package/src/time.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The company's clock. Every date the runtime judges against — a decision's effective days, a
|
|
3
|
+
* delegation's validity — is a date in the company's timezone, not in the host's. A workspace
|
|
4
|
+
* carried between a laptop in Tokyo and a container in UTC has to answer the same question the
|
|
5
|
+
* same way, so the timezone is part of the configuration rather than the environment.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** True when the name is a timezone this runtime knows (`Asia/Tokyo`, `UTC`, ...). */
|
|
9
|
+
export function isTimezone(name: string): boolean {
|
|
10
|
+
try {
|
|
11
|
+
new Intl.DateTimeFormat("en-US", { timeZone: name });
|
|
12
|
+
return true;
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The timezone of the machine, used when the configuration names none. */
|
|
19
|
+
export function hostTimezone(): string {
|
|
20
|
+
const name = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
21
|
+
return name && isTimezone(name) ? name : "UTC";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The business date (`YYYY-MM-DD`) in the company's timezone. */
|
|
25
|
+
export function businessDate(timezone: string, at: Date = new Date()): string {
|
|
26
|
+
// en-CA writes a date as YYYY-MM-DD.
|
|
27
|
+
return new Intl.DateTimeFormat("en-CA", {
|
|
28
|
+
timeZone: timezone,
|
|
29
|
+
year: "numeric",
|
|
30
|
+
month: "2-digit",
|
|
31
|
+
day: "2-digit",
|
|
32
|
+
}).format(at);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The moment as the company reads it: `2026-09-09T14:03:11+09:00`. */
|
|
36
|
+
export function companyTime(timezone: string, at: Date = new Date()): string {
|
|
37
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
38
|
+
timeZone: timezone,
|
|
39
|
+
hour12: false,
|
|
40
|
+
year: "numeric",
|
|
41
|
+
month: "2-digit",
|
|
42
|
+
day: "2-digit",
|
|
43
|
+
hour: "2-digit",
|
|
44
|
+
minute: "2-digit",
|
|
45
|
+
second: "2-digit",
|
|
46
|
+
timeZoneName: "longOffset",
|
|
47
|
+
}).formatToParts(at);
|
|
48
|
+
const of = (type: Intl.DateTimeFormatPartTypes) =>
|
|
49
|
+
parts.find((part) => part.type === type)?.value ?? "";
|
|
50
|
+
// "GMT+09:00" for a zone with an offset, "GMT" for UTC itself.
|
|
51
|
+
const zone = of("timeZoneName").replace("GMT", "");
|
|
52
|
+
const hour = of("hour") === "24" ? "00" : of("hour");
|
|
53
|
+
return `${of("year")}-${of("month")}-${of("day")}T${hour}:${of("minute")}:${of("second")}${zone === "" ? "+00:00" : zone}`;
|
|
54
|
+
}
|